TICS Coding Standard Viewer 
TIOBE Software Quality Framework
Print-friendly version
©TIOBE Software www.tiobe.com
 
C++ Coding Standard
Search

Rule:  FIO30-CChecked automatically with code checker

Synopsis:Exclude user input from format strings
Language:C++
Severity Level:1
Category:Security


Description:

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);
}