...
| Wiki Markup |
|---|
An attacker who can fully or partially control the contents of a format string, can exploitcrash toa crashvulnerable the programprocess, 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|AA. C References#Seacord 05]\] for a detailed explanation of how format string vulnerabilities may be exploited. |
...
This non-compliant code example illustrates the incorrect_password() function which is called during identification and authentication if the specified user is not found or {the password }} is incorrect to display an error message. The function accepts two strings that originated 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: user and password. The function constructs an error message which is then output to stderr using the C99 standard fprintf() function.
| Code Block | ||
|---|---|---|
| ||
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().
Compliant Solution
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 | ||
|---|---|---|
| ||
void incorrect_password(const char *user) {
fprintf(stderr, "%s could not be authenticated.", user);
}
}
|
Non-Compliant Code Example (POSIX)
| Wiki Markup |
|---|
This non-compliant code example is exactly the same as the previous first non-compliant code example, but uses the POSIX function {{syslog()}} \[[Open Group 04|AA. C++ References#Open Group 04]\] instead of the {{fprintf()}} function, which is also susceptible to format string vulnerabilities. |
...
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 outputs the string directly instead of building it and then outputting it.
| Code Block | ||
|---|---|---|
| ||
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 passes the untrusted user input as one of the variadic arguments to syslog() instead of including it in the format string.
| Code Block | ||
|---|---|---|
| ||
void checkincorrect_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 could not be authenticated.", 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 | 3 (high) | 3 (likely) | 3 (low) | P27 | L1 |
...