Skip to content

Latest commit

 

History

History
113 lines (90 loc) · 5.02 KB

exploit_exercises_heap1.md

File metadata and controls

113 lines (90 loc) · 5.02 KB

Exploit exercises - heap1 challenge

Vulnerability

Let's immediately dive into the disassembly:

void main(undefined4 param_1,int param_2)

{
  undefined4 *puVar1;
  void *pvVar2;
  undefined4 *puVar3;
  
  puVar1 = (undefined4 *)malloc(8);
  *puVar1 = 1;
  pvVar2 = malloc(8);
  *(void **)(puVar1 + 1) = pvVar2;
  puVar3 = (undefined4 *)malloc(8);
  *puVar3 = 2;
  pvVar2 = malloc(8);
  *(void **)(puVar3 + 1) = pvVar2;
  strcpy((char *)puVar1[1],*(char **)(param_2 + 4));
  strcpy((char *)puVar3[1],*(char **)(param_2 + 8));
  puts("and that\'s a wrap folks!");
  return;
}

The binary takes our command line arguments (two this time) and copies it into two different memory allocations which reside on the heap. Since strcpy is used for both of those, we can easily overflow from the first chunk into the second making this challenge similar to heap0.

But how can we exactly abuse this to call the winner function? By calling the strcpy function, we are performing a write to a memory address which is specified as a first parameter and the overflow into the second chunk gives us the ability to specify any memory address we want. With this in mind, we can use the first strcpy call to overflow into the second chunk and specify our memory address. Afterwards, we use the second strcpy call to write to that same memory address.

Overflowing and writing

This time, we don't get the helpful output specifying the chunk memory address. We will need to do this by hand via gdb:

Breakpoint 1 at 0x804855a: file heap1/heap1.c, line 34.
(gdb) run AAAAAAAA BBBBBBBB
Starting program: /heap1 AAAAAAAA BBBBBBBB

Breakpoint 1, main (argc=3, argv=0xffffd124) at heap1/heap1.c:34
34	heap1/heap1.c: No such file or directory.
(gdb) i r eax
eax            0x804a190           134521232
(gdb) x/60wx 0x804a190 - 0x40
0x804a150:	0x00000000	0x00000000	0x00000000	0x00000011
0x804a160:	0x00000001	0x0804a170	0x00000000	0x00000011
0x804a170:	0x41414141	0x41414141	0x00000000	0x00000011
0x804a180:	0x00000002	0x0804a190	0x00000000	0x00000011
0x804a190:	0x42424242	0x42424242	0x00000000	0x00021e69
0x804a1a0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1b0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1c0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1d0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1e0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1f0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a200:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a210:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a220:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a230:	0x00000000	0x00000000	0x00000000	0x00000000

Again, breaking it down a little:

0x804a150:	0x00000000	0x00000000	0x00000000	0x00000011
0x804a160:	0x00000001	0x0804a170	0x00000000	0x00000011
0x804a170:	0x41414141	0x41414141	0x00000000	0x00000011
0x804a180:	0x00000002	0x0804a190	0x00000000	0x00000011
0x804a190:	0x42424242	0x42424242	0x00000000	0x00021e69

We see that there are multiple chunks on the heap here. The disassembly shows 4 malloc's being executed, therefore we observe:

  • first malloc chunk puVar1 starts at 0x804a158
  • second malloc chunk pvVar2 starts at 0x804a168
  • third malloc chunk puVar3 starts at 0x804a178
  • fourth malloc chunk pvVar2 (second) starts at 0x804a188

If we think about the content stored in these chunks we can assume that first and second chunk represent a struct:

0x804a158:  0x00000000	0x00000011  (first malloc chunk header)
0x804a160:	0x00000001              (integer, something like id)	
0x804a164:  0x0804a170	            (pointer to string1)
0x804a168:  0x00000000	0x00000011  (second malloc chunk header)
0x804a170:	0x41414141	0x41414141  (our string1)

Same goes for third and fourth chunk:

0x804a178:  0x00000000	0x00000011  (third malloc chunk header)
0x804a180:	0x00000002	            (integer, something like id)
0x804a184:  0x0804a190	            (pointer to string2)
0x804a188:  0x00000000	0x00000011  (fourth malloc chunk header)
0x804a190:	0x42424242	0x42424242  (our string2)

The strcpy is passed with a pointer to our string (string1 or string2, depends whether it is the first or second call). What we want to do here is:

  • write from string1 (0x804a170) all the way down to pointer to string2 (0x804a184) - meaning that we will have to overwrite 0x804a184 - 0x804a170 = 20 bytes (8 bytes string1 + 8 bytes malloc chunk header + 4 bytes integer).
  • in continuation write the memory address where we want to write our string2, but what address is that?

Since in the disassembly the last call that is made after strcpy is:

puts("and that\'s a wrap folks!");

We can simply overwrite the puts GOT entry located at 0x08049774.

To make this complete, we specify the winner function address as a second command argument which will make the second strcpy write the winner function address to puts GOT entry, practically executing the winner function after that second strcpy by calling the winner function instead of puts. Solution for this challenge is solve_exploit_exercises_heap1.py