Sample Test Case  Content

How to Test for Format String Vulnerabilities

Applies to
Compiled code (C/C++)

Summary
Testing for format string vulnerabilities involves the following 4 steps:

1. Identify entry points.
2. Craft attack data for each entry point.
3. Pass attack data to each entry point.
4. Look for application crashes or corrupt output.

1. Identify entry points
Entry points are the means by which you can provide input to the application under test. The following are common entry points:

  • Public APIs
  • Web service methods
  • DCOM methods
  • Network ports
  • UI input fields
  • File input (can take the form of configuration, data, or serialization information)
  • Registry input

As you explore the set of entry points look for the ability to provide string input or data that may be used later in output – either to stdout or to a log – using the printf() family of functions or any other function that can take a format string. A format string is a format specification used by functions such as printf and scanf to replace variables within the string with parameter values passed to the function. The set of functions that include format strings includes:

  • Sprintf
  • _snprintf
  • Printf
  • Fprintf
  • Scanf
  • Fscanf
  • Function #7
  • Function #8
  • etc.


Some examples of string input entry points are:

  • Web application UI input field such as username, password, or search box.
  • Thick client application UI input field such as configuration options, search, or username
  • Public API with a string parameter type
  • String data in a file
  • Entry Point #5
  • Entry Point #6
  • Etc.

There are a couple of common scenarios in which input data may be used in a related to a function call that takes a format string:

  • File names – usually an error message of the type (“Unable to find file %s”) is printed to the log or stdout.
  • Logged entities – such as usernames, request identifiers, and/or methods

For instance, the following code uses a format string to print a file name:

snprintf(buf, BUFSIZE, “Error code %d: File %s not found”, code, s); fprintf (stderr, buf);
 

2.  Craft attack data for each entry point

  • Detailed information similar to #1 "Identify Entry Points"
     

3.  Pass attack data to each entry point.

  • Detailed information similar to #1 "Identify Entry Points"

4.  Look for application crashes or corrupt output.

  • Detailed information similar to #1 "Identify Entry Points"


Repro Example

Flawed Code
In this example, the user input is treated as a filename to be opened. If it is not found, an error message is formatted, with the filename embedded in it. Note the use of snprintf() would prevent a buffer overflow attack. A Format string bug, however, still lurks in the code.

#define BUFSIZE 1024 #define ERR_FILE_NOT_FOUND 42
void error(int code, char *s) { char buf[BUFSIZE]; switch (code) { Case ERR_FILE_NOT_FOUND: snprintf(buf, BUFSIZE, “Error code %d: File %s not found”, code, s); } /* Log to standard Error.. */
fprintf (stderr, buf); /* Also, Possibly write contents of buf to a logfile */ }
int main(int argc, char **argv) { /* attempt to open command line argument as a file */ /* if unsuccessful, report an error */ error(ERR_FILE_NOT_FOUND, argv[1]);

Test Example
To exploit the flawed code above, the following attack string can be used:

  • Passing “Foo%x%x%x%x%x%x%x%x%x%x” as the command line argument will cause a stack pop - revealing itself to the user as corrupt output.
  • Passing “Foo%n%n%n” as the command line argument will write into arbitrary memory – revealing itself to the user as an application crash.

See Also

  • <provides links to other related content available on Web)

Related Items

  • <includes links to other related content within TeamMentor)