Semmle 1.21
Skip to end of metadata
Go to start of metadata

Name: Uncontrolled data used in OS command

Description: Using user-supplied data in an OS command, without neutralizing special elements, can make code vulnerable to command injection.

ID: cpp/command-line-injection

Kind: problem

Severity: error

Precision: low

Query: ExecTainted.ql
/**
 * @name Uncontrolled data used in OS command
 * @description Using user-supplied data in an OS command, without
 *              neutralizing special elements, can make code vulnerable
 *              to command injection.
 * @kind problem
 * @problem.severity error
 * @precision low
 * @id cpp/command-line-injection
 * @tags security
 *       external/cwe/cwe-078
 *       external/cwe/cwe-088
 */
import cpp
import semmle.code.cpp.security.CommandExecution
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking

from Expr taintedArg, Expr taintSource, string taintCause, string callChain
where shellCommand(taintedArg, callChain)
  and tainted(taintSource, taintedArg)
  and isUserInput(taintSource, taintCause)
select
  taintedArg,
  "This argument to an OS command is derived from $@ and then passed to " + callChain,
  taintSource, "user input (" + taintCause + ")"

The code passes user input as part of a call to system or popen without escaping special elements. It generates a command line using sprintf, with the user-supplied data directly passed as a formatting argument. This leaves the code vulnerable to attack by command injection.

Recommendation

Use a library routine to escape characters in the user-supplied string before passing it to a command shell.

Example

The following example runs an external command in two ways. The first way uses sprintf to build a command directly out of a user-supplied argument. As such, it is vulnerable to command injection. The second way quotes the user-provided value before embedding it in the command; assuming the encodeShellString utility is correct, this code should be safe against command injection.

int main(int argc, char** argv) {
  char *userName = argv[2];
  
  {
    // BAD: a string from the user is injected directly into
    // a command line.
    char command1[1000] = {0};
    sprintf(command1, "userinfo -v \"%s\"", userName);
    system(command1);
  }

  {  
    // GOOD: the user string is encoded by a library routine.
    char userNameQuoted[1000] = {0};
    encodeShellString(userNameQuoted, 1000, userName); 
    char command2[1000] = {0};
    sprintf(command2, "userinfo -v %s", userNameQuoted);
    system(command2);
  }
}

References