Main Content

CWE Rule 88

Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')

Since R2024a

Description

Rule Description

The product constructs a string for a command to be executed by a separate component in another control sphere, but it does not properly delimit the intended arguments, options, or switches within that command string.

Polyspace Implementation

The rule checker checks for these issues:

  • Execution of externally controlled command

  • Unsafe call to a system function

Examples

expand all

Issue

This issue occurs when commands are fully or partially constructed from externally controlled input.

Risk

Attackers can use the externally controlled input as operating system commands, or arguments to the application. An attacker could read or modify sensitive data can be read or modified, execute unintended code, or gain access to other aspects of the program.

Fix

Validate the inputs to allow only intended input values. For example, create a list of acceptable inputs and compare the input against this list.

Extend Checker

By default, Polyspace® assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Example — Call External Command
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "unistd.h"
#include "dlfcn.h"
#include "limits.h"
#define MAX 128


void taintedexternalcmd(void)
{
	char* usercmd;
	fgets(usercmd,MAX,stdin);
	char cmd[MAX] = "/usr/bin/cat ";
	strcat(cmd, usercmd);
	system(cmd);//Noncompliant  
}

This example function calls a command from a user input without checking the command variable.

Correction — Use a Predefined Command

One possible correction is to use a switch statement to run a predefined command, using the user input as the switch variable. This corrected code also replaces the call to system() using calls to execve(), which is less unsafe.

#define _XOPEN_SOURCE
#define _GNU_SOURCE

#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "unistd.h"
#include "dlfcn.h"
#include "limits.h"

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
enum { CMD0 = 1, CMD1, CMD2 };

void taintedexternalcmd(void)
{
    int usercmd = strtol(getenv("cmd"), NULL, 10);
    char *argv[3];
    char cmd[SIZE128] = "/usr/bin/cat";
    char arg[SIZE10];

    switch(usercmd) {
        case CMD0:
            strcpy(arg, "*.c");
            break;
        case CMD1:
            strcpy(arg, "*.h");
            break;
        case CMD2:
            strcpy(arg, "*.cpp");
            break;
        default:
            strcpy(arg, "*.c");
    }

    argv[0] = cmd;
    argv[1] = arg;
    argv[2] = NULL;

    execve(cmd, argv, NULL);

    // If execve returns, there was an error
    perror("execve");
    exit(EXIT_FAILURE);
}
Issue

This issue occurs when you use a function that invokes an implementation-defined command processor. These functions include:

  • The C standard system() function.

  • The POSIX popen() function.

  • The Windows® _popen() and _wpopen() functions.

Risk

If the argument of a function that invokes a command processor is not sanitized, it can cause exploitable vulnerabilities. An attacker can execute arbitrary commands or read and modify data anywhere on the system.

Fix

Do not use a system-family function to invoke a command processor. Instead, use safer functions such as POSIX execve() and WinAPI CreateProcess().

Example — system() Called
# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>

enum { 
SIZE512=512,
SIZE3=3};

void func(char *arg)
{
	char buf[SIZE512];
	int retval=sprintf(buf, "/usr/bin/any_cmd %s", arg);
	
	if (retval<=0 || retval>SIZE512){
		/* Handle error */
		abort();
	}
	/* Use of system() to pass any_cmd with 
	unsanitized argument to command processor */
	
	if (system(buf) == -1) { //Noncompliant
	/* Handle error */
  }
} 

In this example, system() passes its argument to the host environment for the command processor to execute. This code is vulnerable to an attack by command-injection.

Correction — Sanitize Argument and Use execve()

In the following code, the argument of any_cmd is sanitized, and then passed to execve() for execution. exec-family functions are not vulnerable to command-injection attacks.

# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>

enum { 
SIZE512=512,
SIZE3=3};


void func(char *arg)
{
  char *const args[SIZE3] = {"any_cmd", arg, NULL};
  char  *const env[] = {NULL}; 
  
  /* Sanitize argument */
  
  /* Use execve() to execute any_cmd. */

  if (execve("/usr/bin/time", args, env) == -1) { 
    /* Handle error */
  }
} 

Check Information

Category: Others

Version History

Introduced in R2024a