Skip to content

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:

  • C and C++ printf and similar methods fprintf, sprintf, snprintf
  • Perl printf and sprintf

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:

The following format string functions can cause runtime errors if the attacker adds conversion specifiers:

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 behavior. 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 behavior 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:

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 the username 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 appropriate 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.