picoCTF 2018: quackme
問題
問題文
Can you deal with the Duck Web? Get us the flag from this program. You can also find the program in /problems/quackme_1_374d85dc071ada50a08b36597288bcfd.
Hints:
Objdump or something similar is probably a good place to start.
問題概要
x86 の ELF ファイルが与えられる.
解答例
指針
- rev は気合
解説
まずは objdump を使って逆アセンブルをしてみる.
main の逆アセンブルコードを読むと, do_magic
という関数が呼ばれているのが分かる.
$ objdump -d -M intel main ... (snip) ... 08048715 <main>: 8048715: 8d 4c 24 04 lea ecx,[esp+0x4] 8048719: 83 e4 f0 and esp,0xfffffff0 804871c: ff 71 fc push DWORD PTR [ecx-0x4] 804871f: 55 push ebp 8048720: 89 e5 mov ebp,esp 8048722: 51 push ecx 8048723: 83 ec 04 sub esp,0x4 8048726: a1 44 a0 04 08 mov eax,ds:0x804a044 804872b: 6a 00 push 0x0 804872d: 6a 02 push 0x2 804872f: 6a 00 push 0x0 8048731: 50 push eax 8048732: e8 79 fd ff ff call 80484b0 <setvbuf@plt> 8048737: 83 c4 10 add esp,0x10 804873a: 83 ec 0c sub esp,0xc 804873d: 68 f0 87 04 08 push 0x80487f0 8048742: e8 29 fd ff ff call 8048470 <puts@plt> 8048747: 83 c4 10 add esp,0x10 804874a: e8 f3 fe ff ff call 8048642 <do_magic> 804874f: 83 ec 0c sub esp,0xc 8048752: 68 bb 88 04 08 push 0x80488bb 8048757: e8 14 fd ff ff call 8048470 <puts@plt> 804875c: 83 c4 10 add esp,0x10 804875f: b8 00 00 00 00 mov eax,0x0 8048764: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4] 8048767: c9 leave 8048768: 8d 61 fc lea esp,[ecx-0x4] 804876b: c3 ret 804876c: 66 90 xchg ax,ax 804876e: 66 90 xchg ax,ax ... (snip) ...
同様に, do_magic
の逆アセンブルコードを読もうとするが結構長くて読むのがとても大変.
IDA Pro というリバースエンジニアリングフレームワークを使うと, かなり解析が楽になるので今回はそれを使う.
IDA で do_magic
を逆アセンブルさせ Graph View で表示させた結果が下の画像である.
以下の処理を先頭から読んでいく.
まず [ebp+var_18]
と [ebp+var_10]
を比べて分岐している.
[ebp+var_18]
, [ebp+var_10]
がどのような値かどうかを調べていく.
do_magic
の先頭から読んでいくと,
var_18= dword ptr -18h var_10= dword ptr -10h
とあり, var_18
および var_10
の初期値が分かる.
また,
call _strlen add esp, 10h mov [ebp+var_10], eax
とあるので, [ebp+var_10]
の値が入力された文字列の長さであると分かる. (関数の戻り値は EAX レジスタに入る)
[ebp+var_18]
の値は一番分かりやすい. 直前に以下の命令があるので, 0 だと分かる.
mov [ebp+var_18], 0
また後半に以下のような命令がありインクリメントされているので, ループ用の変数として機能していると推測できる.
loc_8048707: add [ebp+var_18], 1
次に loc_80486BD
が何をしているのかを解析していく.
アセンブリ命令の右側に命令の意味をコメントとして記載した.
loc_80486BD: mov eax, [ebp+var_18] ; eax = 0 add eax, 8048858h ; eax += 0x8048858 movzx ecx, byte ptr [eax] ; ecx = [eax] が指す値から 1 Bytes 分の値 mov edx, [ebp+var_18] ; edx = 0 mov eax, [ebp+var_14] ; eax = (入力文字列の先頭アドレス) add eax, edx ; eax += edx movzx eax, byte ptr [eax] ; eax = [eax] が指す値から 1 Bytes 分の値 xor eax, ecx ; eax = eax xor ecx mov [ebp+var_1D], al ; [ebp+var_1D] = al(EAXの下位 1 Bytes) mov edx, greetingMessage ; edx = greetingMessage ('You have now entered the Duck Web, and you') mov eax, [ebp+var_18] ; eax = 0 add eax, edx ; eax += [greetingMessage を指すポインタ] movzx eax, byte ptr [eax] ; eax = [eax] が指す値から 1 Bytes 分の値 cmp al, [ebp+var_1D] ; eaxの下位1Bytes と [ebp+var_1D] を比較 jnz short loc_80486EF
[ebp+var_14]
の値は do_magic
の序盤に以下の命令があることから, 入力された文字列の先頭を指すポインタであると分かる.
call read_input mov [ebp+var_14], eax
0x8048858 の指す値から 0x0 が現れるまでの値は以下の通りである. (何も考えずボタンをポチポチすると出てくる)
29 06 16 4F 2B 35 30 1E 51 1B 5B 14 4B 08 5D 2B 53 10 54 51 43 4D 5C 54 5D
var_18
がインクリメントされることを考慮しつつ上記のアセンブリ命令を C言語で表現することを試みる.
int i = 0; int p[] = 0x29, 0x06, 0x16, 0x4F, 0x2B, 0x35, 0x30, 0x1E, 0x51, 0x1B, 0x5B, 0x14, 0x4B, 0x08, 0x5D, 0x2B, 0x53, 0x10, 0x54, 0x51, 0x43, 0x4D, 0x5C, 0x54, 0x5D; char greetingMessage[] = "You have now entered the Duck Web, and you"; char input[]; input(input); int cnt = 0; for (i = 0; i < 0x19; i++) { int a; a = input[i]^p[i]; if (a == greetingMessage[i]) { cnt++; } } if (cnt == 0x19) { puts("Your are winner\n"); }
input[i] ^ p[i] = greetingMessage[i] より
input[i] ^ p[i] ^ p[i] = greetingMessage[i] ^ p[i] <=> input[i] = greetingMessage[i] ^ p[i]
よって, greetingMessage と 0x8048858 が指す値の XOR を取ることで, input の値がわかる.
#!/usr/bin/env python3 greetingMessage = "You have now entered the Duck Web, and you" p = [0x29, 0x06, 0x16, 0x4F, 0x2B, 0x35, 0x30, 0x1E, 0x51, 0x1B, 0x5B, 0x14, 0x4B, 0x08, 0x5D, 0x2B, 0x53, 0x10, 0x54, 0x51, 0x43, 0x4D, 0x5C, 0x54, 0x5D] flag = "" for x, y in zip(greetingMessage, p): flag += chr(ord(x)^y) print (flag)
- 実行結果
$ python solve.py picoCTF{qu4ckm3_6b15c941}
flag: picoCTF{qu4ckm3_6b15c941}