今年も開催されたSECCON for Beginners CTFに参加していました。 私生活のタイミングが悪く短時間しか取り組めませんでしたが、駆け出しCTFerにちょうど良い難易度設定でやりがいのある問題が多かったように思います。 来年もぜひ開催していただきたいです。
Reversing
[warmup] Seccompare
バイナリファイルが渡されるのでIDAで開く。
プログラム実行時の引数である[rsi]とフラグである[rbp+s1]を比較している。
[rbp+var_14]に終端文字0x00が入っているので直前の[rbp+var_15]まで読み進めると
63 74 66 34 62 7b 35 74 72 31 6e 67 73 5f 31 73 5f 6e 30 74 5f 65 6e 30 75 67 68 7d
asciiで読むとctf4b{5tr1ngs_1s_n0t_en0ugh}
でフラグが手に入る。
Linear Operation
バイナリファイルが渡されるのでIDAで開く。 ユーザー入力を[rbp+var_50]に格納してis_correct関数を通した結果が1なら正解ルートに入る。
is_correct関数はlinear_operationの名前通りの長い直列処理が続いている。
複雑なループが無いならangrで解けるのでは? ということでツールに任せたらフラグが手に入った。 angrで解けるパターンと解けないパターンがちょっとわかってきた気がする。
# python 3.5.2
# angr 8.19.4.5
import angr
p = angr.Project("/root/Downloads/seccon4b2019/linear")
state = p.factory.entry_state()
sim = p.factory.simulation_manager(state)
sim.explore(find=(0x400000+0xcf78,), avoid=(0x400000+0xcf86,))
if len(sim.found) > 0:
print(sim.found[0].posix.dumps(0))
Misc
containers
正体不明のファイルが渡されるので識別の手がかりを探す。 末尾にフラグをSHA1でハッシュした文字列が入っていたが普通は逆計算出来ないと思うので別の手がかりを探す。 PNGが複数入っているようなのでこれを当たってみる。
# file containers
containers: data
# strings containers | tail
m:gl
`M_8
`iG~4
F4>
Nl2T
~vWa
hG~4
IEND
VALIDATOR.import hashlib
print('Valid flag.' if hashlib.sha1(input('Please your flag:').encode('utf-8')).hexdigest()=='3c90b7f38d3c200d8e6312fbea35668bec61d282' else 'wrong.'.ENDCONTAINER
# binwalk containers | head
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
16 0x10 PNG image, 128 x 128, 8-bit/color RGBA, non-interlaced
107 0x6B Zlib compressed data, compressed
738 0x2E2 PNG image, 128 x 128, 8-bit/color RGBA, non-interlaced
829 0x33D Zlib compressed data, compressed
1334 0x536 PNG image, 128 x 128, 8-bit/color RGBA, non-interlaced
1425 0x591 Zlib compressed data, compressed
1914 0x77A PNG image, 128 x 128, 8-bit/color RGBA, non-interlaced
ファイルの抽出にはforemostを使う。
# foremost containers
Processing: containers
|*|
# cd /root/Downloads/seccon4b2019/output/png
root@user-VirtualBox:~/Downloads/seccon4b2019/output/png# ls
00000000.png 00000003.png 00000008.png 00000013.png 00000018.png 00000023.png 00000028.png 00000033.png 00000038.png 00000043.png 00000048.png 00000053.png 00000058.png
00000001.png 00000005.png 00000010.png 00000015.png 00000020.png 00000025.png 00000030.png 00000035.png 00000040.png 00000045.png 00000050.png 00000055.png 00000060.png
00000002.png 00000007.png 00000011.png 00000016.png 00000021.png 00000027.png 00000032.png 00000037.png 00000042.png 00000046.png 00000052.png 00000056.png 00000061.png
Dump
未知のファイルが渡されるので識別するとtcpdump。
# file dump
dump: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 262144)
wiresharkで読むとflagが格納されたサーバにwebシェル経由でコマンドを送っている。
URLデコードしてコマンドを読みやすくする。 flagファイルを8進数でダンプしていることが分かる。
従ってサーバから返された文字列037 213 010 ...
を8進数から10進数に戻してバイナリファイルとして扱ってやれば良い。
with open("/root/Downloads/seccon4b2019/dump.txt", "r") as f:
data = f.read()
data = data.replace("\r\n"," ").split(" ")
data = list(map(lambda x:int(x, 8), data))
data = bytes(data)
with open("/root/Downloads/seccon4b2019/dump.out.bin", "wb") as w:
w.write(data)
gzip圧縮されたファイルが入手できるので拡張子をgzに変えてから解凍する。 更にtar圧縮されているので解凍する。
# file dump.out.bin
dump.out.bin: gzip compressed data, last modified: Sun Apr 7 10:46:34 2019, from Unix
# mv dump.out.bin dump.out.bin.gz
# gunzip dump.out.bin.gz
# file dump.out.bin
dump.out.bin: POSIX tar archive
# tar xf dump.out.bin
# ls -la
total 1536
drwxr-xr-x 2 root root 4096 5月 26 16:05 .
drwxr-xr-x 4 root root 4096 5月 26 16:02 ..
-rw-r--r-- 1 root root 767400 5月 26 16:02 dump.out.bin
-rw-r--r-- 1 501 staff 233 4月 7 19:43 ._flag.jpg
-rw-r--r-- 1 501 staff 788359 4月 7 19:43 flag.jpg