Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

Never call any call a formatted I/O function with a format string containing user input.

If the user can control a format string, they can write to arbitrary memory locations. The most common form of this error is in output operation. The rarely used and often forgotten %n format specification causes the number of characters written to be written to a pointer passed on the stack.

Non-compliant Code Example

In the following example, the input is outputted directly as a format string. By putting %n in the input, the user can write arbitrary values to the whatever the stack happens to point to. This can frequently be leveraged to execute arbitrary code. In any case, by including other point operations (such as %s), fprintf() will interpret values on the stack as pointers. This can be used to learn secret information and almost certainly can be used to crash the program.

Code Block

char input[1000];
fgets(input, sizeof(input)-1, stdin);
input[sizeof(input)-1] = '\0';
fprintf(stdout, input);

Non-complaint Code Example

In the following example, the library function syslog() interprets the string msg as a format string, resulting the same security problem as before. This is a common idiom for display the same message multiple locations or the message is difficult to build.

Code Block

void check_password(char *user, char *password) {
  if (strcmpy(password(user), password) != 0) {
    char *msg = malloc(strlen(user) + 100);
    if (!msg) return;
    sprintf (msg, "Wrong password for user %s", user);
    syslog(LOG_INFO, msg);
    free(msg);
  }
}

Complaint Code Example 1

The following example outputs the string directly instead of building it and then outputting it.

Code Block

void check_password(char *user, char *password) {
  if (strcmpy(password(user), password) \!= 0) {
    fprintf (stderr, "Wrong password for user %s", user);
  }
}

Complaint Code Example 2

In this example, the message is built normally, but is then outputted as a string instead of a format string.

Code Block

void check_password(char *user, char *password) {
  if (strcmpy(password(user), password) \!= 0) {
    char *msg = malloc(strlen(user) + 100);
    if (!msg) return;
    sprintf (msg, "Wrong password for user %s", user);
    fprintf (stderr, "%s", user);
    syslog(LOG_INFO, "%s", msg);
    free(msg);
  }
}

Priority: P27 Level: L1

The mismanagement of memory can lead to freeing memory multiple times or writing to already freed memory. Both of these problems can result in an attacker executing arbitrary code with the permissions of the vulnerable process. Memory management errors can also lead to resource depletion and denial-of-service attacks.

Component

Value

Severity

3 (high)

Likelihood

3 (probable)

Remediation cost

3 (low)

References

a tainted value .  An attacker who can fully or partially control the contents of a format string can crash a vulnerable process, view the contents of the stack, view memory content, or write to an arbitrary memory location. Consequently, the attacker can execute arbitrary code with the permissions of the vulnerable process [Seacord 2013b]. Formatted output functions are particularly dangerous because many programmers are unaware of their capabilities. For example, formatted output functions can be used to write an integer value to a specified address using the %n conversion specifier.

Noncompliant Code Example

The incorrect_password() function in this noncompliant code example is called during identification and authentication to display an error message if the specified user is not found or the password is incorrect. The function accepts the name of the user as a string referenced by user. This is an exemplar of untrusted data that originates from an unauthenticated user. The function constructs an error message that is then output to stderr using the C Standard fprintf() function.

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void incorrect_password(const char *user) {
  int ret;
  /* User names are restricted to 256 or fewer characters */
  static const char msg_format[] = "%s cannot be authenticated.\n";
  size_t len = strlen(user) + sizeof(msg_format);
  char *msg = (char *)malloc(len);
  if (msg == NULL) {
    /* Handle error */
  }
  ret = snprintf(msg, len, msg_format, user);
  if (ret < 0) { 
    /* Handle error */ 
  } else if (ret >= len) { 
    /* Handle truncated output */ 
  }
  fprintf(stderr, msg);
  free(msg);
}

The incorrect_password() function calculates the size of the message, allocates dynamic storage, and then constructs the message in the allocated memory using the snprintf() function. The addition operations are not checked for integer overflow because the string referenced by user is known to have a length of 256 or less. Because the %s characters are replaced by the string referenced by user in the call to snprintf(), the resulting string needs 1 byte less than is allocated. The snprintf() function is commonly used for messages that are displayed in multiple locations or messages that are difficult to build. However, the resulting code contains a format-string vulnerability because the msg includes untrusted user input and is passed as the format-string argument in the call to fprintf().

Compliant Solution (fputs())

This compliant solution fixes the problem by replacing the fprintf() call with a call to fputs(), which outputs msg directly to stderr without evaluating its contents:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void incorrect_password(const char *user) {
  int ret;
  /* User names are restricted to 256 or fewer characters */
  static const char msg_format[] = "%s cannot be authenticated.\n";
  size_t len = strlen(user) + sizeof(msg_format);
  char *msg = (char *)malloc(len);
  if (msg == NULL) {
    /* Handle error */
  }
  ret = snprintf(msg, len, msg_format, user);
  if (ret < 0) { 
    /* Handle error */ 
  } else if (ret >= len) { 
    /* Handle truncated output */ 
  }
  fputs(msg, stderr);
  free(msg);
}

Compliant Solution (fprintf())

This compliant solution passes the untrusted user input as one of the variadic arguments to fprintf() and not as part of the format string, eliminating the possibility of a format-string vulnerability:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
void incorrect_password(const char *user) {
  static const char msg_format[] = "%s cannot be authenticated.\n";
  fprintf(stderr, msg_format, user);
}

Noncompliant Code Example (POSIX)

This noncompliant code example is similar to the first noncompliant code example but uses the POSIX function syslog() [IEEE Std 1003.1:2013] instead of the fprintf() function. The syslog() function is also susceptible to format-string vulnerabilities.

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
 
void incorrect_password(const char *user) {
  int ret;
  /* User names are restricted to 256 or fewer characters */
  static const char msg_format[] = "%s cannot be authenticated.\n";
  size_t len = strlen(user) + sizeof(msg_format);
  char *msg = (char *)malloc(len);
  if (msg == NULL) {
    /* Handle error */
  }
  ret = snprintf(msg, len, msg_format, user);
  if (ret < 0) { 
    /* Handle error */ 
  } else if (ret >= len) { 
    /* Handle truncated output */ 
  }
  syslog(LOG_INFO, msg);
  free(msg);
}

The syslog() function first appeared in BSD 4.2 and is supported by Linux and other modern UNIX implementations. It is not available on Windows systems.

Compliant Solution (POSIX)

This compliant solution passes the untrusted user input as one of the variadic arguments to syslog() instead of including it in the format string:

Code Block
bgColor#ccccff
langc
#include <syslog.h>
 
void incorrect_password(const char *user) {
  static const char msg_format[] = "%s cannot be authenticated.\n";
  syslog(LOG_INFO, msg_format, user);
}

Risk Assessment

Failing to exclude user input from format specifiers may allow an attacker to crash a vulnerable process, view the contents of the stack, view memory content, or write to an arbitrary memory location and consequently execute arbitrary code with the permissions of the vulnerable process.

Rule

Severity

Likelihood

Detectable

Repairable

Priority

Level

FIO30-C

High

Likely

Yes

No

P18

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V
 Supported via stubbing/taint analysis
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-FIO30Partially implemented
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

IO.INJ.FMT
MISC.FMT

Format string injection
Format string

Compass/ROSE



Coverity
Include Page
Coverity_V
Coverity_V

TAINTED_STRING

Implemented
Cppcheck Premium
Include Page
Cppcheck Premium_V
Cppcheck Premium_V


premium-cert-fio30-c


GCC
Include Page
GCC_V
GCC_V

Can detect violations of this rule when the -Wformat-security flag is used

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF4916, DF4917, DF4918


Klocwork
Include Page
Klocwork_V
Klocwork_V

SV.FMTSTR.GENERIC
SV.TAINTED.FMTSTR


LDRA tool suite
Include Page
LDRA_V
LDRA_V

86 D

Partially Implemented
Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-FIO30-a
CERT_C-FIO30-b
CERT_C-FIO30-c

Avoid calling functions printf/wprintf with only one argument other than string constant
Avoid using functions fprintf/fwprintf with only two parameters, when second parameter is a variable
Never use unfiltered data from an untrusted user as the format parameter

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

592

Partially supported: reports non-literal format strings

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule FIO30-C

Checks for tainted string format (rule partially covered)

PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V618
Splint
Include Page
Splint_V
Splint_V


Related Vulnerabilities

Two examples of format-string vulnerabilities resulting from a violation of this rule include Ettercap and Samba.

In Ettercap v.NG-0.7.2, the ncurses user interface suffers from a format-string defect. The curses_msg() function in ec_curses.c calls wdg_scroll_print(), which takes a format string and its parameters and passes it to vw_printw(). The curses_msg() function uses one of its parameters as the format string. This input can include user data, allowing for a format-string vulnerability.

The Samba AFS ACL mapping VFS plug-in fails to properly sanitize user-controlled file names that are used in a format specifier supplied to snprintf(). This security flaw becomes exploitable when a user can write to a share that uses Samba's afsacl.so library for setting Windows NT access control lists on files residing on an AFS file system.

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT Oracle Secure Coding Standard for JavaIDS06-J. Exclude unsanitized user input from format stringsPrior to 2018-01-12: CERT: Unspecified Relationship
CERT Perl Secure Coding StandardIDS30-PL. Exclude user input from format stringsPrior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013Injection [RST]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013Including tainted or out-of-domain input in a format string [usrfmt]Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11CWE-134, Uncontrolled Format String2017-05-16: CERT: Exact
CWE 2.11CWE-20, Improper Input Validation2017-05-17: CERT: Rule subset of CWE

Bibliography

[IEEE Std 1003.1:2013]XSH, System Interfaces, syslog
[Seacord 2013b]Chapter 6, "Formatted Output"
[Viega 2005]Section 5.2.23, "Format String Problem"


...

Image Added Image Added Image Added

...