はじめに
CTFの過去問に挑戦してみました。解いた問題はALEXCTFのre2です。使ったツールはgdbとradare2,rabin2です。かなり雑です・・。
それではさっそく...
$ ./re2
Usage: ./re2 flag
$ ./re2 aaaa
Better luck next time
引数として文字列を読み込んでそれによって応答を変えてくるタイプですかね
rabin2による文字列解析
$ rabin2 -z ./re2
000 0x00000e58 0x00400e58 119 120 (.rodata) ascii L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A_{FL4G}_W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t
001 0x00000ed0 0x00400ed0 22 23 (.rodata) ascii Better luck next time
002 0x00000ee8 0x00400ee8 32 33 (.rodata) ascii You should have the flag by now
003 0x00000f09 0x00400f09 7 8 (.rodata) ascii Usage:
004 0x00000f11 0x00400f11 6 7 (.rodata) ascii flag
:
Better luck next time のアドレスは0x00400ed0であることがわかりました。次にradare2を用いてこのアドレスにアクセスする関数を見つけ出します。
radare2による解析
$ r2 ./re2
>aa
>aac
>afl
>axt 0x00400ed0
fcn.00400b56 0x400b5a [DATA] mov esi, str.Better_luck_next_time
fcn.0x400b56であることがわかりました
main関数を見ていきます。
>pdf @ main
:
:
0x00400c20 488945a0 mov qword [local_60h], rax
.-> 0x00400c24 488d45b0 lea rax, [local_50h]
: 0x00400c28 4889c7 mov rdi, rax
: 0x00400c2b e8c0fdffff call sym.std::__cxx11::basic_string_char_std::char_traits_char__std::allocator_char__::end
: 0x00400c30 488945e0 mov qword [local_20h], rax
: 0x00400c34 488d55e0 lea rdx, [local_20h]
: 0x00400c38 488d45a0 lea rax, [local_60h]
: 0x00400c3c 4889d6 mov rsi, rdx
: 0x00400c3f 4889c7 mov rdi, rax
: 0x00400c42 e8f6000000 call fcn.00400d3d
: 0x00400c47 84c0 test al, al
,==< 0x00400c49 744a je 0x400c95
|: 0x00400c4b 488d45a0 lea rax, [local_60h]
|: 0x00400c4f 4889c7 mov rdi, rax
|: 0x00400c52 e843010000 call fcn.00400d9a
|: 0x00400c57 0fb610 movzx edx, byte [rax]
|: 0x00400c5a 488b0d3f1420. mov rcx, qword str.L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A__FL4G__W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t ; [0x6020a0:8]=0x400e58 str.L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A__FL4G__W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t
|: 0x00400c61 8b45ec mov eax, dword [local_14h]
|: 0x00400c64 4898 cdqe
|: 0x00400c66 8b0485c02060. mov eax, dword [rax*4 + 0x6020c0] ; [0x6020c0:4]=36
|: 0x00400c6d 4898 cdqe
|: 0x00400c6f 4801c8 add rax, rcx ; '&'
|: 0x00400c72 0fb600 movzx eax, byte [rax]
|: 0x00400c75 38c2 cmp dl, al
|: 0x00400c77 0f95c0 setne al
|: 0x00400c7a 84c0 test al, al
,===< 0x00400c7c 7405 je 0x400c83
||: 0x00400c7e e8d3feffff call fcn.00400b56
`---> 0x00400c83 8345ec01 add dword [local_14h], 1
|: 0x00400c87 488d45a0 lea rax, [local_60h]
|: 0x00400c8b 4889c7 mov rdi, rax
|: 0x00400c8e e8e7000000 call fcn.00400d7a
|`=< 0x00400c93 eb8f jmp 0x400c24
`--> 0x00400c95 e8d9feffff call fcn.00400b73
:
:
「Better luck next time」なんて煽り文句はもう見たくないのでfcn.00400b56を通らないルートを探します。命令文0x00400c7e周辺に注目すると直前の処理から、ループを行い何か(コマンドライン引数とFlag?)を比較していることがわかります。もとをたどるとal(Flag?)は、rax*4+0x6020c0の値とアドレス0x6020a0を足したアドレスに格納された値である。ここから、gdbに対してバッチ処理を行い、答えを明らかにしていく次第です。
gdbで0x6020c0周辺の値を調べる
gdb-peda$ x/40w 0x6020c0
0x6020c0: 0x00000024 0x00000000 0x00000005 0x00000036
0x6020d0: 0x00000065 0x00000007 0x00000027 0x00000026
0x6020e0: 0x0000002d 0x00000001 0x00000003 0x00000000
0x6020f0: 0x0000000d 0x00000056 0x00000001 0x00000003
0x602100: 0x00000065 0x00000003 0x0000002d 0x00000016
0x602110: 0x00000002 0x00000015 0x00000003 0x00000065
0x602120: 0x00000000 0x00000029 0x00000044 0x00000044
0x602130: 0x00000001 0x00000044 0x0000002b 0x00000000
add.py
offset=[
0x00000024,0x00000000,0x00000005,0x00000036,
0x00000065,0x00000007,0x00000027,0x00000026,
0x0000002d,0x00000001,0x00000003,0x00000000,
0x0000000d,0x00000056,0x00000001,0x00000003,
0x00000065,0x00000003,0x0000002d,0x00000016,
0x00000002,0x00000015,0x00000003,0x00000065,
0x00000000,0x00000029,0x00000044,0x00000044,
0x00000001,0x00000044,0x0000002b,0x00000000]
for i in offset:
address = i+0x400e58
print "x/s "+ hex(address)
add.pyの実行結果をgdbへ
$python add.py | gdb re2
$gdb-peda$ 0x400e7c: "A_{FL4G}_W0nt_b"...
$gdb-peda$ 0x400e58: "L3t_ME_T3ll_Y0u"...
$gdb-peda$ 0x400e5d: "E_T3ll_Y0u_S0m3"...
$gdb-peda$ 0x400e8e: "X4ctly_th4t_345"...
$gdb-peda$ 0x400ebd: "C00l_1F_Y0u_g0t"...
$gdb-peda$ 0x400e5f: "T3ll_Y0u_S0m3th"...
$gdb-peda$ 0x400e7f: "FL4G}_W0nt_b3_3"...
$gdb-peda$ 0x400e7e: "{FL4G}_W0nt_b3_"...
$gdb-peda$ 0x400e85: "W0nt_b3_3X4ctly"...
$gdb-peda$ 0x400e59: "3t_ME_T3ll_Y0u_"...
$gdb-peda$ 0x400e5b: "_ME_T3ll_Y0u_S0"...
$gdb-peda$ 0x400e58: "L3t_ME_T3ll_Y0u"...
$gdb-peda$ 0x400e65: "0u_S0m3th1ng_1m"...
$gdb-peda$ 0x400eae: "v3r_1T_w1ll_b3_"...
$gdb-peda$ 0x400e59: "3t_ME_T3ll_Y0u_"...
$gdb-peda$ 0x400e5b: "_ME_T3ll_Y0u_S0"...
$gdb-peda$ 0x400ebd: "C00l_1F_Y0u_g0t"...
$gdb-peda$ 0x400e5b: "_ME_T3ll_Y0u_S0"...
$gdb-peda$ 0x400e85: "W0nt_b3_3X4ctly"...
$gdb-peda$ 0x400e6e: "1ng_1mp0rtant_A"...
$gdb-peda$ 0x400e5a: "t_ME_T3ll_Y0u_S"...
$gdb-peda$ 0x400e6d: "h1ng_1mp0rtant_"...
$gdb-peda$ 0x400e5b: "_ME_T3ll_Y0u_S0"...
$gdb-peda$ 0x400ebd: "C00l_1F_Y0u_g0t"...
$gdb-peda$ 0x400e58: "L3t_ME_T3ll_Y0u"...
$gdb-peda$ 0x400e81: "4G}_W0nt_b3_3X4"...
$gdb-peda$ 0x400e9c: "5y_t0_c4ptur3_H"...
$gdb-peda$ 0x400e9c: "5y_t0_c4ptur3_H"...
$gdb-peda$ 0x400e59: "3t_ME_T3ll_Y0u_"...
$gdb-peda$ 0x400e9c: "5y_t0_c4ptur3_H"...
$gdb-peda$ 0x400e83: "}_W0nt_b3_3X4ct"...
$gdb-peda$ 0x400e58: "L3t_ME_T3ll_Y0u"...
$gdb-peda$ quit
そして、縦読みにすると...
ようやくフラッグ「ALEXCTF{W3_L0v3_C_W1th_CL45535}」を手に入れました。
最後に
2日間の紆余曲折はなんだったのかと思うど雑な書き込みでしたが、CTFはやはり敷居が高いと感じさせてくれました。
最後のFLAGを手に入れるための処理をもっときれいな方法で行いたかったが、縦読みして答えを得るというおしゃれなことをしてしまった。
Flagの最後のLに関してはよくわかりません。