Exploit development for Format String vulnerability

Format string vulnerability is the result of wrong usage of format functions in C language. Format string vulnerability is the favorite vulnerability of many exploit writers since it provides arbitrary memory overwrite in contrast to stack based buffer overflows where you are just limited to return address overwrite. Moreover format string vulnerability is less known for developers so the chance to find format string vulnerabilities is much more than a typical stack buffer overflow.

Format String vulnerability

Let’s first see one example of format string vulnerability and then explain what you can do with it:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main(int argc, char *argv[]) {

char text[1024];

strcpy(text,  argv[1]);

Printf(text)

}

 

This code allows you to inject an input that leads to an arbitrary memory read to write. To see the vulnerability, just run the program with such an argument:

./a.out AAAA%4\$x

 

As you’re running the program with such argument you see that this string is not outputted and instead you get AAAA string + a hexadecimal value in the output.

This is because this string has a special meaning for a c printf format string function. See this format string to understand the concept:

printf("3th: %3$d, first: %1$05d\n", 10, 20, 30);

 

As you see the first parameter of printf tells the formatting of the output. It tells to print out the 3rd argument(%3$) in front of 3th: text as decimal and then printing first argument in front of first: text as a 5 length decimal.

Can you guess what happens if this printf did not have 3 parameters after the format parameter?

The printf still tries to reach the 3rd argument and it prints whatever is on the stack after second parameter (see Figure 1). Reading 3rd argument from the stack leads to reading whatever inserted before calling the printf function.

format string vulnerability stack layout

You may wonder why this happens since 3rd argument seems to be after the argument. Well, when the compiler compiles this code (and every other codes) it pushes the parameters in reverse order. That’s because in the assembly version, printf reads the parameters like this:

mov parameter1, [sp+8] ; reading first parameter

mov parameter2, [sp+12]; reading second parameter

…

 

Please pay attention that stack grows down and adding larger offset to stack pointer leads to reading the bottom of the stack. If you have still trouble understanding the structure of the stack I recommend read this presentation.

Now it should be easy for you to see why AAAA%4\$x string is a simple exploit for the format string vulnerability example I introduced.

Arbitrary memory read exploit for string format vulnerability

If you want to read a specific memory address a simple AAAA%4\$x exploit does not work for you. You need to pass the address of the memory to read and also you need to somehow make the printf to fetch the memory address you passed. Let’s first find a solution for the latter problem. If we pass the address somehow, we tell the printf to read from that address by using pointers. That’s right by modifying the AAAA%4\$x to AAAA%4\$s we simple tell the printf to read the 4th argument and it is a pointer (%s) to an string. Printf stops reading from the pass address when it reaches the string termination character (null character). Ok now we should find a way to read from an address we pass. If you experiment we different lengths instead of 4 in AAAA%4\$x you see that the output is exactly AAAA! This means you made the printf prints the argument string to the Main function. Remember Main arguments are also on the stack and they are at the bottem of the stack. That’s you just need to pass your address instead of AAAA. This is a modified version of the exploit to read from bffffdd7 memory address:

./a.out  $(printf "\xd7\xfd\xff\xbf")%4\$s

 

Arbitrary memory write exploit for string format vulnerability

Writing to a memory address is possible through the %n format parameter. %n tells the printf to write the number of bytes so far written to a pointer parameter. By manipulating the number of bytes read so far and passing the address to be written, we can easily write to a memory address. Although the most powerful robust exploits can be written using this simple method, modern compilers have removed the %n and unfortunately you have little chance to succeed to arbitrary writing using string format vulnerability. Anyhow below is an example of a string format vulnerability:

./ a.out  $(perl -e 'print "\x94\x97\x04\x08" . "\x95\x97\x04\x08"

. "\x96\x97\x04\x08" . "\x97\x97\x04\x08"')%98x%4\$n%139x%5\$n%258x%6\$n%192x%7\$n

 

 

At first it may seem overwhelming the intention of this exploit, but don’t worry in a sec you understand what’s happening. The first 4 strings after the perl print function is 4 addresses we want to overwrite. The values to be overwritten to these addresses are hex values of:

72

Fd

Ff

Bf

 

If you account the little endianness of the system you probably understand why we want to write these values. Well if successfully overwritten these values consist: bffffd72. We want to write bffffd72 to 08049794. 08049794 points to a 4 byte memory chunk and to write that value we write one of its 4 bytes at a time because bffffd72 is a big number and making n that big leads to writing all of the memory and this certainly causes a crash. Ok to make those 4 values we just can control how many bytes are written. For example look at %98x part of the exploit. This tells the printf to read a value from the stack in hexadecimal and format it to a 98 bytes length so a subsequent %4\$n writes n which is 114 or 0x72 to the 08049794 address. Up to %4\$n, 98 bytes plus the 4 * 4 bytes addresses are written.  After that %139x adds 139 to the 114 so the next value to be written as n is 0xFD. This will be written to the 08049795 and so on.

Conclusion

You see how an arbitrary memory read or write is possible using string overflow vulnerability. You may think this is not as devastating as stack buffer overflow vulnerability, however an arbitrary memory write can lead to a more robust code execution exploit than stack buffer overflow. Just be creative and think of the ways to use this weapon to spawn a shell or execute a command! To name one, you can pass your shellcode instead of using 98 white spaces at the beginning and then write the beginning of shellcode address to a function pointer!

Published in Exploit development
Advanced Programming Concepts
News Letter

Subscribe our Email News Letter to get Instant Update at anytime