Open Flakebi opened 3 months ago
@all-contributors please add @Flakebi for bug, ideas
@kbeyls
I've put up a pull request to add @Flakebi! :tada:
Thank you for sharing this idea to also write about printf-based vulnerabilities! Would you happen to also have any pointers to how compilers help mitigate against exploits using these vulnerabilities?
clang has a warning for this (-Wformat-security
, seems to be on by default; gcc didn’t complain on this code):
<source>:31:12: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
31 | printf(argv[0]);
| ^~~~~~~
<source>:31:12: note: treat the string as an argument to avoid this
31 | printf(argv[0]);
| ^
| "%s",
Relatedly, I just found the owasp site for format string attacks, but it doesn’t have that much information: https://owasp.org/www-community/attacks/Format_string_attack
printf
can be used for exploits if an attacker controls the format string:There are three main format options that can be used for exploitation, the first two are well-known, the third one is rather esoteric, but essential for exploiting:
"%p"
leaks argument registers and stack memory."%s"
dereferences a pointer (in argument registers or on the stack) and leaks the memory until the first zero-byte in memory."%n"
writes to a pointer (from argument registers or on the stack).Different registers or memory can be leaked by repeating format options, e.g.
"%p %p %p %p %p %p %s"
. POSIX (and therefore glibc) supports argument reordering, allowing to replace the repetition by"%5$p"
to print the 5th argument and jumping back and forth between arguments.Now to the most important format option,
"%n"
. When%n
is found in a format string,printf
interprets the argument as a pointer and writes the number of printed characters so far to this pointer. This makes a nice write gadget, as the written number can be controlled by prepending garbage output of the needed length, e.g. with"%100p%n"
to write a100
. The size of the written value can be controlled by specifying a precision for"%n"
.With this much, we still need a pointer on the stack that points to something useful which we want to overwrite. Luckily, one possibility that is always there are frame pointers, which conveniently point directly to another address that is also located on the stack. A possible pattern is, use a frame pointer to overwrite the few least-significant bytes of the next frame pointer with a controlled value, making this second frame pointer point to a controlled location. A second
"%n"
in the string makes use of the second frame pointer to overwrite a more interesting memory location.If there are two or more attacker controlled
printf
s in a row (or a loop of them), the first one can be used to leak information about stack layout, existing pointers and so on (leaking a return address breaks ASLR). The secondprintf
can then be used to change the memory content based on the leaked information.