CertC++-FIO30¶
Exclude user input from format strings
Required inputs: IR
Never call a formatted I/O function with a format string containing 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.
#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:
#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:
#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.
#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:
#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 | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| FIO30-C | High | Likely | Medium | P18 | L1 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT Oracle Secure Coding Standard for Java | IDS06-J. Exclude unsanitized user input from format strings | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CERT Perl Secure Coding Standard | IDS30-PL. Exclude user input from format strings | Prior to 2018-01-12: CERT: Unspecified Relationship |
| ISO/IEC TR 24772:2013 | Injection [RST] | Prior to 2018-01-12: CERT: Unspecified Relationship |
| ISO/IEC TS 17961:2013 | Including tainted or out-of-domain input in a format string [usrfmt] | Prior to 2018-01-12: CERT: Unspecified Relationship |
| CWE 2.11 | CWE-134, Uncontrolled Format String | 2017-05-16: CERT: Exact |
| CWE 2.11 | CWE-20, Improper Input Validation | 2017-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" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
tainted_string |
Exclude user input from format strings. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
forbidden_one_argument_calls¶
forbidden_one_argument_calls : set[bauhaus.analysis.config.QualifiedName] = {'printf', 'wprintf'}
forbidden_two_argument_calls¶
forbidden_two_argument_calls : set[bauhaus.analysis.config.QualifiedName] = {'fprintf', 'fwprintf', 'syslog'}