Use of Externally-Controlled Format String
Description
Use of Externally-Controlled Format String (Format String Vulnerability) occurs when user-supplied input is used directly as the format string argument in functions like printf(), sprintf(), fprintf(), and similar formatting functions. Format specifiers like %s, %x, and %n in printf-family functions can be exploited to read from arbitrary memory locations (%s, %x), crash programs, or write to arbitrary memory locations (%n). The %n specifier is particularly dangerous as it writes the count of bytes printed so far to an address provided on the stack, enabling arbitrary write primitives.
Risk
Format string vulnerabilities are extremely dangerous, providing attackers with powerful exploitation primitives comparable to buffer overflows. Using %x repeatedly allows reading stack contents, potentially exposing return addresses (defeating ASLR), canary values, and sensitive data. The %n specifier enables arbitrary writes to memory, allowing attackers to overwrite GOT entries, return addresses, or function pointers for code execution. Entire books have been written on format string exploitation techniques. The vulnerability has been ranked in the top 10 most-reported vulnerability types and affects hundreds of applications in CVE databases.
Solution
Never pass user-controlled input directly as a format string. Always use a static format string with the user input as an argument: printf("%s", user_input) instead of printf(user_input). Enable compiler warnings for format string issues (-Wformat-security, -Wformat, -Wformat-nonliteral in GCC/Clang). Use format string checking attributes on custom formatting functions. Consider using safer alternatives that don't support %n. Apply static analysis tools that detect format string vulnerabilities. In C++, prefer type-safe formatting like std::format or fmt library.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Information Disclosure Format specifiers %s and %x allow reading arbitrary memory locations on the stack and potentially beyond, exposing sensitive data. |
| Access Control | Scope: Code Execution The %n specifier provides an arbitrary write primitive, enabling overwrite of return addresses, GOT entries, or function pointers. |
| Availability | Scope: Denial of Service Invalid format specifiers or dereferencing invalid addresses causes crashes. |
Example Code + Solution Code
Vulnerable Code
#include <stdio.h>
// VULNERABLE: User input as format string
void log_message(char *user_input) {
printf(user_input); // User can inject %x, %s, %n
}
// VULNERABLE: Format string in syslog
void log_error(char *error_msg) {
syslog(LOG_ERR, error_msg); // Same vulnerability
}
// VULNERABLE: Indirect format string
void display_username(char *username) {
char buffer[256];
// User-controlled format string
sprintf(buffer, username); // Vulnerable!
display(buffer);
}
/*
* Attack examples:
* Input: "%x %x %x %x" -> Leaks stack values
* Input: "%s" -> Reads string at stack address (may crash)
* Input: "%n" -> Writes to address on stack
* Input: "AAAA%08x.%08x.%08x.%08x.%n" -> Write-what-where attack
*/
Fixed Code
#include <stdio.h>
#include <syslog.h>
// SAFE: User input as argument, not format string
void log_message_safe(const char *user_input) {
printf("%s", user_input); // User input cannot inject specifiers
}
// SAFE: Format string for syslog
void log_error_safe(const char *error_msg) {
syslog(LOG_ERR, "%s", error_msg); // Fixed format string
}
// SAFE: Proper sprintf usage
void display_username_safe(const char *username) {
char buffer[256];
// Use format specifier for user data
snprintf(buffer, sizeof(buffer), "User: %s", username);
display(buffer);
}
// SAFE: C++ type-safe formatting (C++20)
#ifdef __cplusplus
#include <format>
#include <string>
void log_cpp(const std::string& user_input) {
// Type-safe formatting - no format string injection possible
auto msg = std::format("Message: {}", user_input);
std::cout << msg << std::endl;
}
#endif
// Compile with format warnings: gcc -Wformat -Wformat-security -Werror
Exploited in the Wild
ProFTPD Format String (ProFTPD, 1999-2000)
The discovery of format string vulnerabilities as an attack vector came from a security audit of ProFTPD in September 1999. The first major public exploits providing remote root access were published in June 2000 on Bugtraq, marking the beginning of widespread format string exploitation.
PHP Format String Vulnerabilities (PHP, Multiple)
CVE-2011-1153 and related vulnerabilities fixed 27 format string issues in PHP at once, demonstrating how pervasive this vulnerability class became in software development.
Wu-FTPd Format String (Wu-FTPd, 2000)
One of the most widely exploited early format string vulnerabilities, affecting a popular FTP server daemon used on many Unix systems, enabling remote root compromise.
Tools to test/exploit
-
GDB + PEDA/GEF — debugger extensions for format string exploit development.
-
pwntools — CTF framework with format string exploitation utilities.
-
Format String Exploitation Tools — specialized tools for format string attacks.
CVE Examples
-
CVE-2011-1153 — PHP multiple format string vulnerabilities.
-
CVE-2012-0809 — sudo format string vulnerability enabling privilege escalation.
-
CVE-2022-42043 — PDF-XChange format string vulnerability.
References
-
MITRE. "CWE-134: Use of Externally-Controlled Format String." https://cwe.mitre.org/data/definitions/134.html
-
CERT. "FIO30-C. Exclude user input from format strings." https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C
-
Wikipedia. "Uncontrolled format string." https://en.wikipedia.org/wiki/Uncontrolled_format_string