You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 41 Next »

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

An attacker who can fully or partially control the contents of a format string, can exploit to crash the program, 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. See [[Seacord 05]] for a detailed explanation of how format string vulnerabilities may be exploited.

Formatted output functions are particularly dangerous because many programmers are unaware of their capabilities (for example, they can write an integer value to a specified address using the %n conversion specifier).

Non-Compliant Code Example

This non-compliant code example illustrates the incorrect_password() function which is called during identification and authentication if user is not found or {password}} is incorrect to display an error message. The function accepts two strings that originated from an untrusted, unauthenticated user: user and password. The function constructs an error message which is then output to stderr using the C99 standard fprintf() function.

void incorrect_password(const char *user, const char *password) {
  /* user names are restricted to 256 characters or less */
  size_t len = strlen(user) + sizeof("%s could not be authenticated.") - 1;
  char *msg = (char *)malloc(len);
  if (!msg) {
    /* handle error condition */
  }
  snprintf(msg, len, "%s could not be authenticated.", user);
  fprintf(stderr, msg);
  free(msg);
}

The incorrect_password() function constructs msg in dynamically allocated memory by first calculating the size of the message, allocating dynamic storage, and then constructing the message in the allocated memory using the snprintf())) function. The addition operations are not checked for integer overflow, because the length of the string referenced by {{user is known to be have a length of 256 or less. Because the %s characters are replaced by the string referenced by {user in the call to snprintf(), one less byte is required to store the resulting string and terminating null byte character. This is a common idiom for displaying the same message in multiple locations or when the message is difficult to build. The resulting code contains a format string vulnerability, however, because the msg includes untrusted user input and is passed as the format string argument in the call to fprintf().

Non-Compliant Code Example (POSIX)

This non-compliant code example is exactly the same as the previous example, but uses the POSIX function syslog() [[Open Group 04]] instead of the fprintf() function, which is also susceptible to format string vulnerabilities.

void incorrect_password(const char *user, const char *password) {
  /* user names are restricted to 256 characters or less */
  size_t len = strlen(user) + sizeof("%s could not be authenticated.") - 1;
  char *msg = (char *)malloc(len);
  if (!msg) {
    /* handle error condition */
  }
  snprintf(msg, len, "%s could not be authenticated.", user);
  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

This compliant solution outputs the string directly instead of building it and then outputting it.

void check_password(const char *user, const char *password) {
  if (strcmp(lookup_password(user), password) != 0) {
    fprintf (stderr, "%s password incorrect", user);
  }
}

Compliant Solution 2

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

void check_password(const char *user, const char *password) {
  if (strcmp(lookup_password(user), password) != 0) {
    size_t len = strlen(user) + 100;
    char *msg = (char *)malloc(len);
    if (!msg) {
      /* handle error condition */
    }
    snprintf(msg, len, "%s password incorrect", user);
    fprintf(stderr, "%s", user);
    syslog(LOG_INFO, "%s", msg);
    free(msg);
  }
}

Risk Assessment

Failing to exclude user input from format specifiers may allow an attacker to execute arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO30-C

3 (high)

3 (likely)

3 (low)

P27

L1

Two recent 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 [[VU#286468]]. The Samba AFS ACL mapping VFS plug-in fails to properly sanitize user-controlled filenames that are used in a format specifier supplied to snprintf(). This security flaw becomes exploitable when a user is able to 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.

Related Vulnerabilities

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

References

[[ISO/IEC 9899-1999]] Section 7.19.6, "Formatted input/output functions"
[[MITRE 07]] CWE ID 134, "Uncontrolled Format String"
[[Open Group 04]] syslog()
[[Seacord 05]] Chapter 6, "Formatted Output"
[[Viega 05]] Section 5.2.23, "Format string problem"
[[VU#286468]]
[[VU#649732]]


FIO15-A. Avoid taking conditional actions based on path or file names      09. Input Output (FIO)       FIO31-C. Do not simultaneously open the same file multiple times

  • No labels