Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I was cleaning up some code today on the advice of someone I know, and was made aware of the potential for a hacker to gain control over the flow of the process using format strings, particularly the "%n" option.

I'm not entirely sure, both conceptually and practically, how this would be achieved.

Am I right in thinking it has something to do with overwriting return addresses? Assuming I know where I'm writing to, %n still only gives me the number of characters, so surely buffer overflow prevention would render this as next to impossible? Or is there a way to move the number 0xbbff to the memory location 0xb7001122 with a string like 0xb7001122 0xbbff %n?

There's relatively little about it online! I'm quite confused!

share|improve this question

2 Answers 2

up vote 4 down vote accepted

To understand what is going on, the important point is that in C, functions do not "know" how many arguments the caller gave them. For normal functions, the compiler detects invalid usages. For instance, if you write this:

#include <string.h>
/* ... */
    memcpy(a, b);

then the compiler will complain loudly, and refuse to finish compilation: the included header <string.h> declares the memcpy() function as taking three parameters (two pointers, and an integer of type size_t). If the compiler then sees a call to memcpy() with only two parameters, the mismatch will make him scream.

However, some functions take a variable number of parameters, e.g. printf(). For these functions, the compiler cannot do much checking. printf() uses as first argument a "format string" whose contents tell printf() how many other arguments it should find, and their expected type. When the format string is a literal constant, smart compilers can do some extra checks (with gcc, this is done with the "format" attribute, a gcc-specific extension). But it cannot do anything when the format string is obtained at runtime. Imagine, for instance, the following code:

printf(s);

where s is a string which the attacker can choose (e.g. some data sent over the network, or an invocation parameter). The programmer just assumed that the string was "plain" (only alphanumeric characters) but the attacker can put "%" signs in it, which printf() interprets by looking at extra parameters. There are no extra parameters, so printf() will actually look at the memory slots where extra parameters would have been, had there been any. These slots are "lower in the stack" (technically, at higher addresses, since stacks "grow down" in most architectures); local variables, function return address, and other stack elements for the current function (and its caller, and its caller's caller, and so on) are found there.

By using %u specifiers, the attacker can read all these slots (printf() will dutifully print out their contents), and that's already bad. With the %n parameter, the powers of the attacker are even enhanced: %n takes the next parameter, interprets it as a pointer value, follows it, and writes at the pointed-to address the current number of characters so far (the number of characters printed out by printf() up to the %n). If the faulty printf() call occurs in a function where there is a local variable which is also under the control of the attacker (e.g. a simple integer value), then the attacker can make a %n to use that local variable as a pointer, and write data where it points to. In shorter words, this gives the attacker the ability to write whatever bytes he wants, wherever he wants in RAM. At that point, you are pretty much doomed.

The correct code would have been:

printf("%s", s);

which then forced printf() to print the attacker-controlled string "as is", without doing anything special with the "%" characters.


This printf() vulnerability highlights the fact that C is a rather dangerous language: no bounds checks, no strong type checks... you can make the program look at memory slots as if they were parameters, while they are not, and also interpret byte patterns as if they were pointers, which they are not. Competent C programmers will write their code so as to give the compiler as most information as possible, so as to detect errors (security holes are just programming errors with an effect that can be twisted to the attacker's advantage). But competence is a scarce resource, and even the best programmers occasionally do mistakes.

share|improve this answer

The format string exploitation process depends on how the format string is being used in the vulnerable application. For example printf() is only reading memory, which may enable the attacker to read an ASLR'ed address or canary value that could be used in conjugation with a buffer overflow to gain control of the process. If the application is using sprintf() then the application is writing to memory starting at a specific base address. By supplying too many format string options such at %n the attacker may be able to overwrite memory outside of the bounds of the destination address. If the destination is is a local variable declared on the stack, then it maybe possible for the attacker to corrupt the stack frame and gain control of the instruction pointer.

These attacks and many more are covered in Exploiting Software: How to break code and Hacking: The Art of Exploitation.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.