OtterCTF: Gotta Go Deeper
問題
問題文
Rick and morty played with the configurations of the portal gun and accidentally got stuck in this picture.
Help us get them out.
問題概要
png ファイルが与えられる.
解答例
指針
- binwalk + 青空白猫
解説
与えられたファイルを binwalk を使って解析した.
binwalk はファイルの中に埋め込まれたファイルを確認, 抽出することできる.
https://github.com/ReFirmLabs/binwalk
同様のことができるツールとして, foremost があるが, 今回 foremost を使った場合, 埋め込まれたファイルのうち一つだけ, 見つけ出すことができなかった.
$ binwalk GottaGoDeeper.png DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 952 x 536, 8-bit/color RGB, non-interlaced 140 0x8C Zlib compressed data, best compression 517387 0x7E50B PNG image, 960 x 640, 8-bit/color RGB, non-interlaced 517540 0x7E5A4 Zlib compressed data, best compression 1355842 0x14B042 TIFF image data, big-endian, offset of first image directory: 8 1355982 0x14B0CE PNG image, 2186 x 3300, 8-bit/color RGBA, non-interlaced 1356067 0x14B123 Zlib compressed data, best compression 1358910 0x14BC3E Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#"> 1359017 0x14BCA9 Unix path: /ns.adobe.com/xap/1.0/mm/" 1359075 0x14BCE3 Unix path: /ns.adobe.com/xap/1.0/sType/ResourceEvent#" 1359150 0x14BD2E Unix path: /ns.adobe.com/xap/1.0/sType/ResourceRef#" 1359220 0x14BD74 Unix path: /purl.org/dc/elements/1.1/" 1379914 0x150E4A Zlib compressed data, best compression 8965470 0x88CD5E PNG image, 1172 x 501, 8-bit/color RGB, non-interlaced 8965623 0x88CDF7 Zlib compressed data, best compression 9207969 0x8C80A1 TIFF image data, big-endian, offset of first image directory: 8 9208109 0x8C812D PNG image, 1920 x 1081, 8-bit/color RGB, non-interlaced 9461212 0x905DDC TIFF image data, big-endian, offset of first image directory: 8
-D オプションを付与すると, 拡張子をつけて抽出できる.
$ binwalk -D 'png image:png' GottaGoDeeper.png (snip) $ ls _GottaGoDeeper.png.extracted/ 0.png 14B123 150E4A 7E50B.png 7E5A4.zlib 88CDF7 8C 8C.zlib 14B0CE.png 14B123.zlib 150E4A.zlib 7E5A4 88CD5E.png 88CDF7.zlib 8C812D.png
Window マシンから sftp を使って, 抽出したファイルを Windows マシンに転送した.
caster は私の Linux のマシンの hostname.
$ sftp caster Connected to caster. sftp> get -r Downloads/_GottaGoDeeper.png.extracted/ Fetching /home/kira/Downloads/_GottaGoDeeper.png.extracted/ to _GottaGoDeeper.png.extracted Retrieving /home/kira/Downloads/_GottaGoDeeper.png.extracted /home/kira/Downloads/_GottaGoDeeper.png.extracted/8C 100% 1495KB 4.9MB/s 00:00 (snip) sftp>
得られた png ファイルは以下のようなものである.
- 0.png
- 7E50B.png
- 8C812D.png
- 14B0CE.png
- 88CD5E.png
png ファイルにはそれぞれ文字列が書かれており, これは base64 でエンコードされた文字列の一部であると推測できる.
14B0CE.png には base64 でエンコードされた文字列一部が書かれていないので, うさみみハリケーンに同梱されている汎用ファイルアナライザ, 青い空を見上げればいつもそこに白い猫 で解析した.
うさみみハリケーンのインストールについては以下のリンクが参考になる.
「うさみみハリケーン」のダウンロード・インストール・使用方法(messiahcat氏提供)
うさみみハリケーンは Windows でしか動作しないため, わざわざ sftp を用いて Windows マシンにファイルを転送した,
14B0CE.png を青空白猫で解析すると以下のような文字列が一瞬で見つかった.
これまで見つかった文字列をまとめると次のようになる.
7E50B.png => TWICE RnlaWDA9 8C812D.png => ZmxhZ 14B0CE.png => ZXJXZU 88CD5E.png => tEZWVw
7E50B.png に書かれている TWICE という文字列の意味を最初は 2 回用いると解釈したがこれは誤りで, base64 で 2 回 encode されたことを意味していた.
$ echo RnlaWDA9 | base64 -d FyZX0=
次に, FyZX0=, ZmxhZ, ZXJXZU, tEZWVw を並び替えてつなげた文字列を base64 decode することを考える. FyZX0= は末尾に "=" があることから末尾の文字列であると推測できる.
itertools の permutation を用いて並び替えて base64 デコードを試すがうまくいかなかった.
#!/usr/bin/env python3 # coding: utf-8 from base64 import b64decode from itertools import permutations L = ["RnlaWDA9", "ZmxhZ", "ZXJXZU", "tEZWVw"] for i in list(permutations(L[1:])): i = list(i) i += [b64decode(L[0]).decode("utf-8")] s = "".join(i) try: ret = b64decode(s) print (ret) except: print ("Error")
- 実行結果
$ python solve.py Error Error Error Error Error Error
一文字だけ欠けていると推測 (guessing) しその一文字 brute-force すると flag が得られた.
#!/usr/bin/env python3 # coding: utf-8 from base64 import b64decode from itertools import permutations T = [] for i in range(26): T += [chr(ord('A') + i)] T += [chr(ord('a') + i)] for i in "0123456789+/": T += [i] L = ["RnlaWDA9", "ZmxhZ", "ZXJXZU", "tEZWVw"] for t in T: for i in list(permutations(L[1:] + [t])): i = list(i) i += [b64decode(L[0]).decode("utf-8")] s = "".join(i) try: ret = b64decode(s) if not "\\x" in str(ret) and "flag" in str(ret): print (ret.decode("utf-8")) except: print ("Error")
- 実行結果
flag+DeeperWeAre} erWeKDeepflag!re} flag;DeeperWeAre} erWeKDeepflag1re} flagKDeeperWeAre} erWeKDeepflagAre} flag[DeeperWeAre} erWeKDeepflagQre} flagkDeeperWeAre} erWeKDeepflagare} flag{DeeperWeAre} erWeKDeepflagqre}
flag: flag{DeeperWeAre}