llsoftsec / llsoftsecbook

Low-Level Software Security for Compiler Developers
https://llsoftsec.github.io/llsoftsecbook/
Other
514 stars 50 forks source link

Add section about printf exploits #241

Open Flakebi opened 3 months ago

Flakebi commented 3 months ago

printf can be used for exploits if an attacker controls the format string:

char *input = ; // attacker controlled
print(input);

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:

  1. "%p" leaks argument registers and stack memory.
  2. "%s" dereferences a pointer (in argument registers or on the stack) and leaks the memory until the first zero-byte in memory.
  3. "%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 a 100. 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 printfs 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 second printf can then be used to change the memory content based on the leaked information.

kbeyls commented 3 months ago

@all-contributors please add @Flakebi for bug, ideas

allcontributors[bot] commented 3 months ago

@kbeyls

I've put up a pull request to add @Flakebi! :tada:

kbeyls commented 3 months ago

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?

Flakebi commented 3 months ago

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