SECCON Beginners CTF 2020 writeup(その2)
続きです。開催中に解けなかった問題も含みます。
前編はこちら。
ctf4bba.hatenablog.com
yakisoba
名前がスパゲッティのもじりな通り、騙しコードが大量に入っている。
自動化?angr?何それおいしいの?ということで、IDAで解いた。
rdi+0, 1, 2…と入力した文字を1文字ずつ比較している。読みづらいが法則性があり、
次の文字を代入する直前には必ず正解の文字と比較している。
(例えば、上の図で言うと、movzx edx, byte ptr [rdi+2]
の直前で比較している't'は正しい2文字目である)
flag: ctf4b{sp4gh3tt1_r1pp3r1n0}
追記
angrで解いた。
In [2]: import angr In [3]: p = angr.Project('./yakisoba') In [5]: find = (0x400000+0x6d2, ) In [6]: avoid = (0x400000+0x6f7, ) In [7]: state = p.factory.entry_state() ...: sim = p.factory.simulation_manager(state) ...: sim.explore(find=find, avoid=avoid) Out[7]: <SimulationManager with 7 active, 1 found, 175 avoid> In [8]: print(sim.found[0].posix.dumps(0)) b'ctf4b{sp4gh3tt1_r1pp3r1n0}\x00\xd9\xd9\xd9\xd9'
ghost
コマンドでぐぐっていたらGhostscript(PostScript)というものだとわかった。
スタックベースのコードは初めて見るのでしんどかった。挙動を見比べながらpythonに落とし込んだ。
しかし間に合わなかった。
enc_flag = ['3417', '61039', '39615', '14756', '10315', '49836', '44840', '20086', '18149', '31454', '35718', '44949', '4715', '22725', '62312', '18726', '47196', '54518', '2667', '44346', '55284', '5240', '32181', '61722', '6447', '38218', '6033', '32270', '51128', '6112', '22332', '60338', '14994', '44529', '25059', '61829', '52094'] In [65]: char = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}!' In [66]: stack = 1 In [67]: answer = '' In [68]: count = 0 In [69]: for e in enc_flag: ...: for c in char: ...: num = ((count + 1) ^ ord(c)) * stack ...: for i in range(462): ...: num = ((count + 1) ^ ord(c)) * stack * num % 64711 ...: if int(e) == num: ...: answer += c ...: stack = num % 128 + 1 ...: count += 1 ...: break ...: In [70]: answer Out[70]: 'ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fun!}'
writeup読んで、なるほどgsコマンドにやらせればよかったのか…
flag: ctf4b{st4ck_m4ch1n3_1s_4_l0t_0f_fun!}
unzip
解けなかった。
ファイル閲覧のURLがいかにもディレクトリトラバーサルできそうだが、コードを読むと、
アップロードしたzipファイルに含まれるファイル名を$_SESSIONに保存し、そのファイル名しか通さないようになっている。
(ので、いきなり../../flag.txtを指定しても"no such file"になってしまう)
statIndex($i)はzipファイルのエントリ情報を出力するので、"..@..@flag.txt"みたいな適当な名前でファイルを作成してzip圧縮し、
バイナリエディタで開いて"../../flag.txt"に書き換えれば良いらしい。
なるほど、作ったzipファイルをunzip -lコマンドで見ても下記のようになる。
% unzip -l flag.zip Archive: flag.zip Length Date Time Name --------- ---------- ----- ---- 4 05-24-2020 04:49 ../../flag.txt --------- ------- 4 1 file
flag: ctf4b{y0u_c4nn07_7ru57_4ny_1npu75_1nclud1n6_z1p_f1l3n4m35}
siblangs
Androidは得意分野なのに解けなくて悔しいです!!!!
まずはファイルをunzip。出てきたdexファイルをdex2jarでjarファイルに変換し、JD-GUIで開く。
その中にValidateFlagModuleという怪しいクラスがある(これに開催期間中気づけなかった…やっぱ徹夜は良くないね)
謎の数値列とAES暗号化処理が見える。
from Crypto.Cipher import AES s = [95, -59, -20, -93, -70, 0, -32, -93, -23, 63, -9, 60, 86, 123, -61, -8, 17, -113, -106, 28, 99, -72, -3, 1, -41, -123, 17, 93, -36, 45, 18, 71, 61, 70, -117, -55, 107, -75, -89, 3, 94, -71, 30] enc = '' for si in s: if si < 0: enc += hex(si & 0xFF)[2:].zfill(2) else: enc += hex(si)[2:].zfill(2) key = "IncrediblySecure".encode('utf-8') cipher = AES.new(key, AES.MODE_GCM, data[:12]) print(cipher.decrypt(data[12:-16]))
これが後半部分。負値の16進数への変換とその後のbytes列の扱いがうまくいかなくて変なところでつまづいた。
(もっとスマートな方法あるっぽいけどわかりやすさ重視で…)
あと、この機会にカビが生えてたPyCryptoをPyCryptodomeに置き換えた。
前半部分は開催期間中に解けてた。
"ctf4b"でgrepすると、下記の部分が見つかる。
si
読むと"AkeyForios10.3"をキーに謎の数値列をxorしていることがわかる。
(1文字ずつ手作業でxorしたのでスクリプト無し)
flag: ctf4b{jav4_and_j4va5cr1pt_3verywhere}
sneaky
解けなかった。ので、writeupを読んでやった結果を備忘録。
ハイスコアを取れば良いらしい。
lea r13, aScoreD ; "SCORE: %d"
mov rsi, [rbx+20h] mov rdi, r13 xor eax, eax call sub_407F80
この2カ所から、[rbx+20h]がおそらく%dに入る(=スコア)と思われる。
$ gdb ./sneaky gdb-peda$ b *0x401373 <- mov rsi, [rbx+20h] Breakpoint 1 at 0x401373 gdb-peda$ start gdb-peda$ run
途中でsys_cloneで何度も止まるのでcontinue 10000とか入れて飛ばした。
この時[rbx+20h]は0x73e600。
gdb-peda$ x/2wx $rbx+0x20 0x73e600: 0x00000000 0x00000000 gdb-peda$ set *0x73e604 = 0x31000000 gdb-peda$ x/2wx $rbx+0x20 0x73e600: 0x00000000 0x31000000
QWORDなので8バイト目(リトルエンディアン)を1にしてみて、runしてみたらflag。
flag: ctf4b{still_ez_2_cheat?}