CTF for BBA

ゲーム時々CTFやるBBAの日常。

SECCON Beginners CTF 2020 writeup(その2)

続きです。開催中に解けなかった問題も含みます。

前編はこちら。
ctf4bba.hatenablog.com


yakisoba

名前がスパゲッティのもじりな通り、騙しコードが大量に入っている。
自動化?angr?何それおいしいの?ということで、IDAで解いた。

rdi+0, 1, 2…と入力した文字を1文字ずつ比較している。読みづらいが法則性があり、
次の文字を代入する直前には必ず正解の文字と比較している。
f:id:boxwolf:20200530183738p:plain
(例えば、上の図で言うと、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すると、下記の部分が見つかる。

sif:id:boxwolf:20200530170711p:plain

読むと"AkeyForios10.3"をキーに謎の数値列をxorしていることがわかる。
(1文字ずつ手作業でxorしたのでスクリプト無し)

flag: ctf4b{jav4_and_j4va5cr1pt_3verywhere}


sneaky

解けなかった。ので、writeupを読んでやった結果を備忘録。
ハイスコアを取れば良いらしい。

f:id:boxwolf:20200530174335p:plain

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とか入れて飛ばした。

f:id:boxwolf:20200530181427p:plain
この時[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?}