In part one of Format String Vulnerabilities I showed you some simple code with a very serious format string vulnerability. I showed you how you could exploit this vulnerability to read any part of memory. In section two, I’m going to show you how you can write to any address in memory!
Writing to Memory
Exploiting format string vulnerabilities is all about providing input that uses a format character that expects its value to be passed by reference and you control that reference. I used ‘%s’ to read from memory. I’m going to use %n to write to memory.
%n Number of characters written by this printf.
Lucky for us, there is a really easy way to control the number of characters written by printf. When you specify a format character, you can optionally give it an integer for the width of the format character.
%#x Number of characters prepended as padding.
We can use this to control how many characters are written by printf.
If you’re following at this point, you’re probably wondering how we’re going to use %n to write to memory. We can use %n to write the integer value of our 4 byte target address one byte at a time.
We can write the lower order bits and shift the target address by a byte, utilizing width padding characters to control the integer value we write to a given byte.
Target = 0xAAAA1111 0xAAAA1111 = <int> 0xAAAA1112 = <int> 0xAAAA1113 = <int> 0xAAAA1114 = <int>
If we can overwrite an address, we can control the flow of execution. Let’s overwrite the address of printf! First we need to find the address for printf
nobody@nobody:~$ objdump -R ./text_to_print ... 08049660 R_386_JUMP_SLOT printf ...
Next we need to craft a string to overwrite this address using %n and width padding. We’ll also use printf’s ability to argument swap:
One can also specify explicitly which argument is taken by writing '%m$' instead of '%'
Lastly, instead of writing a byte at a time, we can use printf’s ‘length modifier’ to tell printf what type it’s writing %n too. We’ll use ‘l’ (ell) for long unsigned int.
0x0000beef seems like a good address to overwrite printf with since everyone loves beef! Our input will be a 4 byte address (to printf) and the decimal value of beef – 4 (to accommodate the length of the address) = 48875.
Here’s what It looks like when we run it (note we have to escape the $ with a for the shell):
nobody@nobody:~$ ./text_to_print $(python -c 'print "x60x96x04x08"')%48875x%4$ln Program received signal SIGSEGV, Segmentation fault. 0x0000beef in ?? ()
You can see that the program tried to jump to 0x0000beef and crashed! Be sure to check out the full section on format string vulnerabilities in Hacking: The Art of Exploitation, it talks all about these techniques and more.