Format string bugs
TL;DR: When
printf-family functions consume an attacker-controlled format string,%s/%pgive arbitrary read of stack memory and%ngives arbitrary write — a clean primitive for ASLR leak + GOT overwrite chains.
What it is
printf(user_input) instead of printf("%s", user_input) lets the format parser walk the variadic argument area (registers, then stack) as if the attacker supplied the arguments. %x/%p/%s leak whatever sits in those slots; %n writes the number of characters printed so far to a pointer pulled from the same argument slot. Combine them and you get arbitrary read and arbitrary 1/2/4-byte write at chosen addresses.
Preconditions / where it applies
- A
*printf,syslog,err,warn,vfprintf, or similar function called with attacker-controlled format. - Position-relative or absolute target address reachable from the stack as a pulled argument (with
%N$direct-index syntax this is most stack slots). - Binary lacks
-Wformat=2 -Wformat-securitybuild hardening or runtime FORTIFY checking; classic Linux ELF on glibc.
Technique
- Find the offset. Send
AAAA|%p|%p|%p|...and count which slot prints0x41414141— that is your%N$index. - Leak. Pull saved instruction pointers, libc pointers, canary bytes:
1 2
%7$p # leak slot 7 %6$s # deref slot 6 as C string (arbitrary read)
- Arbitrary write with
%n. Place target address on the stack as part of the format string, then use width specifiers to control the printed count:1
<addr><addr+2>%<low>x%K$hn%<high>x%L$hn
Two
%hnwrites are easier than one%nbecause the width counter would otherwise need to reach a billion-plus. - Common targets: GOT entry of
printfitself → next call jumps to controlled address;__free_hook/__malloc_hookon older glibc; return address on stack;exit_funcspointers. - pwntools helper:
fmtstr_payload(offset, {addr: value})builds the payload, including endian-correct addresses and width math.
Detection and defence
- Compile with
-Wformat -Wformat-security -Werror=format-security; modern glibc +_FORTIFY_SOURCE>=2rejects%nto writable memory at runtime. - Static analysis (Coverity, CodeQL
cpp/tainted-format-string) flags non-literal format arguments cheaply. - Logging libraries should mandate parameterised format strings; ban
printf(buf)patterns in code review. - Crashes inside
vfprintfwith weird counts in the format-state struct are a tell at triage time.
References
- OWASP format string — concept summary
- pwntools fmtstr docs —
fmtstr_payloadautomation - HackTricks format string — write-ups and tricks
See also: ret2libc, srop, stack-buffer-overflow.