Format string bugs happen when user-controlled input is passed
directly as the format argument to a function like printf, instead
of as a data argument.
// Vulnerable
printf(user_input);
// Safe
printf("%s", user_input);
Reading Memory
Every %x or %p in the format string pops a value off the stack
(or from registers, depending on the calling convention) and prints
it — even though no argument was actually passed. This alone lets an
attacker leak stack contents, pointers, and sometimes canary values.
Writing Memory
The %n specifier writes the number of bytes output so far to the
address of its corresponding argument. Combined with precise width
specifiers (%08x, padding tricks), this becomes an arbitrary write
primitive — powerful enough to overwrite saved return addresses or
GOT entries.
Mitigations
- Compilers now warn (
-Wformat-security) when a non-literal string is passed as a format argument. - FORTIFY_SOURCE can detect some format string misuse at runtime.
- The real fix is simple: never pass untrusted input as a format string, full stop.
Takeaway
Format string bugs are rarer in new code today, but they’re a great way to build intuition for how the stack and calling conventions work — intuition that carries over directly into other binary exploitation techniques.