超超beginnerな私がLeakageを解いた過程をここに残します.
実行してみると
usage: ./leakage flag
と出力.
とりあえず適当な文字を引数に再度実行してみると
$ ./leakage a wrong $ ./leakage aaa wrong $ ./leakage 1234 wrong
次に, radare2
でmain
関数を逆アセンブルして呼び出してみました.
[0x00400660]> pdf ;-- main: / (fcn) main 111 | main (); | ; var int local_10h @ rbp-0x10 | ; var int local_4h @ rbp-0x4 | ; DATA XREF from 0x0040051d (entry0) | 0x00400660 55 push rbp | 0x00400661 4889e5 mov rbp, rsp | 0x00400664 4883ec10 sub rsp, 0x10 | 0x00400668 897dfc mov dword [local_4h], edi | 0x0040066b 488975f0 mov qword [local_10h], rsi | 0x0040066f 837dfc01 cmp dword [local_4h], 1 ; rflags ; [0x1:4]=-1 | ,=< 0x00400673 7f22 jg 0x400697 | | 0x00400675 488b45f0 mov rax, qword [local_10h] | | 0x00400679 488b00 mov rax, qword [rax] | | 0x0040067c 4889c6 mov rsi, rax | | 0x0040067f 488d3dfd0500. lea rdi, qword str.usage:__s_flag ; 0x400c83 ; "usage: %s flag\n" ; const char * format | | 0x00400686 b800000000 mov eax, 0 | | 0x0040068b e860feffff call sym.imp.printf ; int printf(const char *format) | | 0x00400690 b801000000 mov eax, 1 | ,==< 0x00400695 eb36 jmp 0x4006cd | || ; JMP XREF from 0x00400673 (main) | |`-> 0x00400697 488b45f0 mov rax, qword [local_10h] | | 0x0040069b 4883c008 add rax, 8 | | 0x0040069f 488b00 mov rax, qword [rax] | | 0x004006a2 4889c7 mov rdi, rax | | 0x004006a5 e83dffffff call sym.is_correct | | 0x004006aa 85c0 test eax, eax | |,=< 0x004006ac 740e je 0x4006bc | || 0x004006ae 488d3dde0500. lea rdi, qword str.correct ; 0x400c93 ; "correct" ; const char * s | || 0x004006b5 e806feffff call sym.imp.puts ; int puts(const char *s) | ,===< 0x004006ba eb0c jmp 0x4006c8 | ||| ; JMP XREF from 0x004006ac (main) | ||`-> 0x004006bc 488d3dd80500. lea rdi, qword str.wrong ; 0x400c9b ; "wrong" ; const char * s | || 0x004006c3 e8f8fdffff call sym.imp.puts ; int puts(const char *s) | || ; JMP XREF from 0x004006ba (main) | `---> 0x004006c8 b800000000 mov eax, 0 | | ; JMP XREF from 0x00400695 (main) | `--> 0x004006cd c9 leave \ 0x004006ce c3 ret
sym.is_correct
を呼び出して, その結果次第でstr.correct
かstr.wrong
に分かれています.
次にsym.is_correct
の逆アセンブルの結果をみました.
[0x004005e7]> pdf / (fcn) sym.is_correct 121 | sym.is_correct (); | ; var int local_18h @ rbp-0x18 | ; var int local_5h @ rbp-0x5 | ; var int local_4h @ rbp-0x4 | ; CALL XREF from 0x004006a5 (main) | 0x004005e7 55 push rbp | 0x004005e8 4889e5 mov rbp, rsp | 0x004005eb 4883ec20 sub rsp, 0x20 | 0x004005ef 48897de8 mov qword [local_18h], rdi | 0x004005f3 488b45e8 mov rax, qword [local_18h] | 0x004005f7 4889c7 mov rdi, rax ; const char * s | 0x004005fa e8d1feffff call sym.imp.strlen ; size_t strlen(const char *s) | 0x004005ff 4883f822 cmp rax, 0x22 ; '"' ; 34 | ,=< 0x00400603 7407 je 0x40060c | | 0x00400605 b800000000 mov eax, 0 | ,==< 0x0040060a eb52 jmp 0x40065e | || ; JMP XREF from 0x00400603 (sym.is_correct) | |`-> 0x0040060c c745fc000000. mov dword [local_4h], 0 | |,=< 0x00400613 eb3e jmp 0x400653 | || ; JMP XREF from 0x00400657 (sym.is_correct) | .---> 0x00400615 8b45fc mov eax, dword [local_4h] (省略)
sym.imp.strlen
でコマンドライン引数の大きさを計算し, それと0x22
(34)を比較してます.
デバッグモードで再度引数をctf4b{aaaaaaaaaaaaaaaaaaaaaaaaaaa}
(34文字)にして実行してみました.
;-- rip: 0x004005e7 55 push rbp 0x004005e8 4889e5 mov rbp, rsp 0x004005eb 4883ec20 sub rsp, 0x20 0x004005ef 48897de8 mov qword [rbp - 0x18], rdi 0x004005f3 488b45e8 mov rax, qword [rbp - 0x18] 0x004005f7 4889c7 mov rdi, rax 0x004005fa e8d1feffff call sym.imp.strlen ;[1] 0x004005ff 4883f822 cmp rax, 0x22 ; '"' ; 34 ,=< 0x00400603 7407 je 0x40060c ;[2] | 0x00400605 b800000000 mov eax, 0 ,==< 0x0040060a eb52 jmp 0x40065e ;[3] |`-> 0x0040060c c745fc000000. mov dword [rbp - 4], 0 |,=< 0x00400613 eb3e jmp 0x400653 ;[4] .---> 0x00400615 8b45fc mov eax, dword [rbp - 4] :|| 0x00400618 4863d0 movsxd rdx, eax :|| 0x0040061b 488d053e0600. lea rax, qword obj.enc_flag ; 0x400c60 :|| 0x00400622 0fb60402 movzx eax, byte [rdx + rax] :|| 0x00400626 0fb6c0 movzx eax, al :|| 0x00400629 89c7 mov edi, eax :|| 0x0040062b e8a0000000 call sym.convert ;[5] :|| 0x00400630 8845fb mov byte [rbp - 5], al :|| 0x00400633 8b45fc mov eax, dword [rbp - 4] :|| 0x00400636 4863d0 movsxd rdx, eax :|| 0x00400639 488b45e8 mov rax, qword [rbp - 0x18] :|| 0x0040063d 4801d0 add rax, rdx ; '(' :|| 0x00400640 0fb600 movzx eax, byte [rax] :|| 0x00400643 3845fb cmp byte [rbp - 5], al ; [0x2:1]=255 ; 2 ,====< 0x00400646 7407 je 0x40064f ;[6] |:|| 0x00400648 b800000000 mov eax, 0 ,=====< 0x0040064d eb0f jmp 0x40065e ;[3] |`----> 0x0040064f 8345fc01 add dword [rbp - 4], 1 | :|`-> 0x00400653 837dfc21 cmp dword [rbp - 4], 0x21 ; [0x21:4]=-1 ; '!' ; 33 | `===< 0x00400657 7ebc jle 0x400615 ;[7] | | 0x00400659 b801000000 mov eax, 1 `--`--> 0x0040065e c9 leave
すると, sym.convert
の後
cmp byte [rbp - 5], al ; [0x2:1]=255 ; 2
ここの結果次第で, main
に戻るか戻らないか分かられていたので, ここのレジスタの中身を見ました.
[0x00400643]> px @ rbp - 5 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x7ffe9568e0cb 6300 0000 00f0 e068 95fe 7f00 00aa 0640 c......h.......@ (省略)
下位1byteは'c'ですね.
[0x00400643]> dr al 0x00000063
0x03
はASCIIコード
ではc
よって一致しています.
このような比較を1文字ずつ, 計34文字比較します. すると
[0x00400643]> px @ rbp - 5 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x7ffe9568e0cb 6c06 0000 00f0 e068 95fe 7f00 00aa 0640 l......h.......@
下位1byteはl
ですね.
[0x00400643]> dr al 0x00000061
0x61
はASCIIコード
ではa
不一致である為wrong
と返されます. そこで, al
の値を0x6c
(l
)に変更します.
このようにal
の値をbyte [rbp - 5]
に揃えていく操作を最後まで続けます.
すると, 最終的にflag
ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}
のゲットです!お疲れ様でした!
[追伸] 他の方のwrite upを読んでると, angr
という自動解析ツールでさらっと(?)解いてますね.
次回までに習得しておこうと思います.