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

Compare with Current View Page History

« Previous Version 107 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 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 2005a].

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.)

Noncompliant Code Example

This noncompliant code example shows the incorrect_password() function, which 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 null-terminated byte string referenced by user. This is an excellent example of data that originates from an untrusted, unauthenticated user. The function constructs an error message that is then output to stderr using the C standard fprintf() function [ISO/IEC 9899:2011].

void incorrect_password(const char *user) {
  int ret;
  /* user names are restricted to 256 characters or less */
  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 length of 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(), 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().

Compliant Solution (fputs())

This compliant solution fixes the problem by replacing the fprintf() call with a call to fputs(), which does not treat msg like a format string but outputs it to stderr as is.

void incorrect_password(const char *user) {
  int ret;
  /* user names are restricted to 256 characters or less */
  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 */ ;

  if (fputs(msg, stderr) == EOF) {
    /* Handle error */
  }
  free(msg);
}

Compliant Solution (fprintf())

This simpler 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.

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 exactly the same as the first noncompliant code example but uses the POSIX function syslog() [Open Group 2004] instead of the fprintf() function, which is also susceptible to format-string vulnerabilities.

void incorrect_password(const char *user) {
  int ret;
  /* user names are restricted to 256 characters or less */
  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.

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

Remediation Cost

Priority

Level

FIO30-C

high

likely

medium

P18

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.

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.

Automated Detection

Tool

Version

Checker

Description

Fortify SCA

V. 5.0

  

Splint

3.1.1  
GCC4.3.5 

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

Compass/ROSE

 

 

 

Klocwork

2024.1

SV.FMTSTR.GENERIC
SV.TAINTED.FMTSTR

 

LDRA tool suite

9.7.1

86 D

Partially implemented.

Related Vulnerabilities

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

Related Guidelines

CERT C++ Secure Coding Standard: FIO30-CPP. Exclude user input from format strings

CERT Oracle Secure Coding Standard for Java: IDS06-J. Exclude user input from format strings

CERT Perl Secure Coding Standard: IDS30-PL. Exclude user input from format strings

ISO/IEC 9899:2011 Section 7.21.6, "Formatted input/output functions"

ISO/IEC PDTR 24772 "RST Injection"

ISO/IEC TR 17961  (Draft) Including tainted or out-of-domain input in a format string [usrfmt]

MITRE CWE: CWE-134, "Uncontrolled Format String"

Bibliography

[Open Group 2004] syslog()
[Seacord 2005] Chapter 6, "Formatted Output"
[Viega 2005] Section 5.2.23, "Format String Problem"


  • No labels