WSTG - Stable
Testing for Format String Injection
ID |
---|
WSTG-INPV-13 |
Summary
A format string is a null-terminated character sequence that also contains conversion specifiers interpreted or converted at runtime. If server-side code concatenates a user’s input with a format string, an attacker can append additional conversion specifiers to cause a runtime error, information disclosure, or buffer overflow.
The worst case for format strings vulnerabilities occur in languages that don’t check arguments and also include a %n
specifier that writes to memory. These functions, if exploited by an attacker modifying a format string, could cause information disclosure and code execution:
These format string functions cannot write to memory, but attackers can still cause information disclosure by changing format strings to output values the developers did not intend to send:
- Python 2.6 and 2.7 str.format and Python 3 unicode str.format can be modified by injecting strings that can point to other variables in memory
The following format string functions can cause runtime errors if the attacker adds conversion specifiers:
- Java String.format and PrintStream.format
- PHP printf
The code pattern that causes a format string vulnerability is a call to a string format function that contains unsanitized user input. The following example shows how a debug printf
could make a program vulnerable:
The example in C:
char *userName = /* input from user controlled field */;
printf("DEBUG Current user: ");
// Vulnerable debugging code
printf(userName);
The example in Java:
final String userName = /* input from user controlled field */;
System.out.printf("DEBUG Current user: ");
// Vulnerable code:
System.out.printf(userName);
In this particular example, if the attacker set their userName
to have one or more conversion specifiers, there would be unwanted behaviour. The C example would print out memory contents if userName
contained %p%p%p%p%p
, and it can corrupt memory contents if there is a %n
in the string. In the Java example, a username
containing any specifier that needs an input (including %x
or %s
) would cause the program to crash with IllegalFormatException
. Although the examples are still subject to other problems, the vulnerability can be fixed by printf arguments of printf("DEBUG Current user: %s", userName)
.
Test Objectives
- Assess whether injecting format string conversion specifiers into user-controlled fields causes undesired behaviour from the application.
How to Test
Tests include analysis of the code and injecting conversion specifiers as user input to the application under test.
Static Analysis
Static analysis tools can find format string vulnerabilities in either the code or in binaries. Examples of tools include:
- C and C++: Flawfinder
- Java: FindSecurityBugs rule FORMAT_STRING_MANIPULATION
- PHP: String formatter Analyzer in phpsa
Manual Code Inspection
Static analysis may miss more subtle cases including format strings generated by complex code. To look for vulnerabilities manually in a codebase, a tester can look for all calls in the codebase that accept a format string and trace back to make sure untrusted input cannot change the format string.
Conversion Specifier Injection
Testers can check at the unit test or full system test level by sending conversion specifiers in any string input. Fuzz the program using all of the conversion specifiers for all languages the system under test uses. See the OWASP Format string attack page for possible inputs to use. If the test fails, the program will crash or display an unexpected output. If the test passes, the attempt to send a conversion specifier should be blocked, or the string should go through the system with no issues as with any other valid input.
The examples in the following subsections have a URL of this form:
https://vulnerable_host/userinfo?username=x
- The user-controlled value is
x
(for theusername
parameter).
Manual Injection
Testers can perform a manual test using a web browser or other web API debugging tools. Browse to the web application or site such that the query has conversion specifiers. Note that most conversion specifiers need encoding if sent inside a URL because they contain special characters including %
and {
. The test can introduce a string of specifiers %s%s%s%n
by browsing with the following URL:
https://vulnerable_host/userinfo?username=%25s%25s%25s%25n
If the web site is vulnerable, the browser or tool should receive an error, which may include a timeout or an HTTP return code 500.
The Java code returns the error
java.util.MissingFormatArgumentException: Format specifier '%s'
Depending on the C implementation, the process may crash completely with Segmentation Fault
.
Tool Assisted Fuzzing
Fuzzing tools including wfuzz can automate injection tests. For wfuzz, start with a text file (fuzz.txt in this example) with one input per line:
fuzz.txt:
alice
%s%s%s%n
%p%p%p%p%p
{event.__init__.__globals__[CONFIG][SECRET_KEY]}
The fuzz.txt
file contains the following:
- A valid input
alice
to verify the application can process a normal input - Two strings with C-like conversion specifiers
- One Python conversion specifier to attempt to read global variables
To send the fuzzing input file to the web application under test, use the following command:
wfuzz -c -z file,fuzz.txt,urlencode https://vulnerable_host/userinfo?username=FUZZ
In the above call, the urlencode
argument enables the approprate escaping for the strings and FUZZ
(with the capital letters) tells the tool where to introduce the inputs.
An example output is as follows
ID Response Lines Word Chars Payload
===================================================================
000000002: 500 0 L 5 W 142 Ch "%25s%25s%25s%25n"
000000003: 500 0 L 5 W 137 Ch "%25p%25p%25p%25p%25p"
000000004: 200 0 L 1 W 48 Ch "%7Bevent.__init__.__globals__%5BCONFIG%5D%5BSECRET_KEY%5D%7D"
000000001: 200 0 L 1 W 5 Ch "alice"
The above result validates the application’s weakness to the injection of C-like conversion specifiers %s
and %p
.