Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

The C function Standard system() function executes a specified command by invoking an implementation-defined command processor, such as a UNIX shell or CMD.EXE in Windows NT and later [ISO/IEC 9899:2011]Microsoft Windows. The POSIX popen() function also invokes  and Windows _popen() functions also invoke a command processor but creates create a pipe between the calling program and the executed command, returning a pointer to a stream that can be used to either read from or write to the pipe [Open Group 2004IEEE Std 1003.1:2013].

External programs are commonly invoked to perform a function required by the overall system. This is a form of reuse and might even be considered a crude form of component-based software engineering.

 

Use of the system() function can result in exploitable vulnerabilities, in the worst case allowing execution of arbitrary system commands. Situations in which calls to system() have high risk include the following: 

  • When passing an unsanitized or improperly sanitized command string originating from a tainted source
  • If a command is specified without a path name and the command processor path name resolution mechanism is accessible to an attacker
  • If a relative path to an executable is specified and control over the current working directory is accessible to an attacker
  • If the specified executable program can be spoofed by an attacker

Do not invoke a command processor via system() or equivalent functions to execute a command. Command interpreters, such as the POSIX shell sh and the Microsoft Windows command-language interpreter CMD.EXE, however, provide functionality in addition to executing a simple command. If this functionality is not required, it is a good idea to avoid the system() function or any other function that invokes a command interpreter because such functions significantly complicate the command-string sanitization. (See ENV03-C. Sanitize the environment when invoking external programs.)

Noncompliant Code Example

In this noncompliant code example, the system() function is used to execute any_cmd in the host environment. Invocation of a command processor is not required.

Code Block
bgColor#ffcccc
languagec
langc
char *input#include <string.h>
#include <stdlib.h>
#include <stdio.h>

enum { BUFFERSIZE = NULL512 };

/* input gets initialized by user */

void func(const char *input) {
  char cmdbuf[512BUFFERSIZE];
  int len_wanted = snprintf(cmdbuf, BUFFERSIZE,
  cmdbuf, sizeof(cmdbuf),                            "any_cmd '%s'", input
);
  if (len_wanted >= sizeof(cmdbufBUFFERSIZE)) {
    perror("Input too long");
}
 /* Handle error */
  } else if (len_wanted < 0) {
    perror("Encoding error");
}
/* Handle error */
  } else if (system(cmdbuf) == -1) {
    /* Handle  perror("Error executing input");error */
  }
}

If this code is compiled and run with superuser elevated privileges on a Linux system, for example, an attacker can create an account by entering the following string:

...

The shell would interpret this string as two separate commands:

Code Block
any_cmd 'happy';
useradd 'attacker'

and create a new user account that the attacker can use to access the compromised system.

Please note that this This noncompliant code example also violates STR02-C. Sanitize data passed to complex subsystems.

Compliant Solution (POSIX)

In this compliant solution, the call to system() is replaced with a call to execve(). The exec() family of functions can be used to run external executables in a variety of ways depending on the function and parameters useddoes not use a full shell interpreter, so it is not vulnerable to command-injection attacks, such as the one illustrated in the noncompliant code example.

The execlp(), execvp(), and (nonstandard) execvP() functions duplicate the actions of the shell in searching for an executable file if the specified file name does not contain a forward slash 'character (/' character). As a result, they should only be used without a forward slash character (/) only if the PATH environment variable has been is set to a safe value, as described in ENV03-C. Sanitize the environment when invoking external programs.

The execl(), execle(), execv(), and execve() functions do not perform pathname path name substitution.

The exec() functions do not use a full shell interpreter, so they are not vulnerable to command-injection attacks, such as the one illustrated in the noncompliant code example.

Additionally, precautions should be taken to ensure that the external executable cannot be modified by an untrusted user, for example, by ensuring the executable is not writable by the user.

Code Block
bgColor#ccccff
languagec
char *input = NULL;

/* input gets initialized by user */

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
 
void func(char *input) {
  pid_t pid;
  int status;
  pid_t ret;
  char *const args[3] = {"any_exe", input, NULL};
  char **env;
  extern char **environ;

  /* ... Sanitize arguments ... */

  pid = fork();
  if (pid == -1) {
    perror("fork error");
}
/* Handle error */
  } else if (pid != 0) {
    while ((ret = waitpid(pid, &status, 0)) == -1) {
      if (errno != EINTR) {
       perror("Error waiting for child process"); /* Handle error */
        break;
      }
    }
    if ((ret !== -10) &&||
        !(!WIFEXITED(status) ||&& !WEXITSTATUS(status)) ) {
      /* Report unexpected child status */
    }
  } else {

    /* ... Initialize env as a sanitized copy of environ ... */

    if (execve("/usr/bin/any_execmd", args, env) == -1) {
    perror("Error executing any_exe");   /* Handle error */
      _exitExit(127);
    }
  }
}

This compliant solution is significantly different from the equivalent preceding noncompliant code example. First, input is incorporated into the args array and passed as an argument to execve(). This eliminates any , eliminating concerns about buffer overflow or string truncation while forming the command string. Second, this compliant solution must fork forks a new process before executing "/usr/bin/any_execmd" in the child process. While Although this method is more complicated than calling system(), the added security is worth the additional effort.

The exit status of 127 is the value set by the shell when a command is not found, and POSIX recommends that applications should do the same. XCU, Section 2.8.2 [Open Group 2004, of Standard for Information Technology—Portable Operating System Interface (POSIX®), Base Specifications, Issue 7 [IEEE Std 1003.1:2013], says:

If a command is not found, the exit status shall be 127. If the command name is found, but it is not an executable utility, the exit status shall be 126. Applications that invoke utilities without using the shell should use these exit status values to report similar errors.

Compliant Solution (Windows)

This compliant solution uses the Microsoft Windows CreateProcess() API:

Code Block
bgColor#ccccff
languagec
#include <Windows.h>

void func(TCHAR *input) {
  STARTUPINFO si = { 0 };
  PROCESS_INFORMATION pi;
  si.cb = sizeof(si);
  if (!CreateProcess(TEXT("any_cmd.exe"), input, NULL, NULL, FALSE,
                     0, 0, 0, &si, &pi)) {
    /* Handle error */
  }
  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
}

This compliant solution relies on the input parameter being non-const. If it were const, the solution would need to create a copy of the parameter because the CreateProcess() function can modify the command-line arguments to be passed into the newly created process.

This solution creates the process such that the child process does not inherit any handles from the parent process, in compliance with WIN03-C. Understand HANDLE inheritance.

Noncompliant Code Example (POSIX)

This noncompliant code invokes the C system() function to remove the .config file in the user's home directory.

Code Block
bgColor#FFcccc
languagec
#include <stdlib.h>
 
void func(void) {
  system("rm ~/.config");
}

If the vulnerable program has superuser elevated privileges, an attacker can manipulate the value of the HOME so environment variable such that this program can remove any file named .config anywhere on the system.

Compliant Solution (POSIX)

One way to eliminate a An alternative to invoking the system() call that executes to execute an external program to perform a function required by the program operation is to implement the functionality directly in the program , preferably with using existing library calls. For example, one way This compliant solution calls the POSIX unlink() function to remove a file without using system() is to use the POSIX unlinkinvoking the system() function [Open Group 2004].IEEE Std 1003.1:2013]

Code Block
bgColor#ccccff
languagec
#include <pwd.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void func(void) {
  const char *file_format = "%s/foo.config";
const  size_t len;
  char *filepathname;
  struct passwd *pwd;

  /* Get /etc/passwd entry for current user */
  pwd = getpwuid(getuid());
  if (pwd == NULL) {
    /* Handle error */
  return 1;
}

  /* buildBuild full path name home dir from pw entry */

  len = strlen(pwd->pw_dir) + strlen(file_format) + 1;
file  pathname = (char *)malloc(len+1);
snprintf(file  if (NULL == pathname) {
    /* Handle error */
  }
  int r = snprintf(pathname, len, file_format, pwd->pw_dir);
  if (r < 0 || r >= len) {
    /* Handle error */
  }
  if (unlink(filepathname) != 0) {
    /* Handle error in unlink */
  }

  free(filepathname);
file = NULL;}

Be careful using unlink(), particularly when running with elevated privileges, because it may be susceptible to file-related race conditionsThe unlink() function is not susceptible to a symlink attack where the final component of pathname (the file name) is a symbolic link because unlink() will remove the symbolic link and not affect any file or directory named by the contents of the symbolic link. (See FIO01-C. Be careful using functions that use file names for identification.)  While this reduces the susceptibility of the unlink() function to symlink attacks, it does not eliminate it.  The unlink() function is still susceptible if one of the directory names included in the pathname is a symbolic link.  This could cause the unlink() function to delete a similarly named file in a different directory.

Compliant Solution (Windows)

This compliant solution uses the Microsoft Windows SHGetKnownFolderPath() API to get the current user's My Documents folder, which is then combined with the file name to create the path to the file to be deleted. The file is then removed using the DeleteFile() API.

Code Block
bgColor#ccccff
languagec
#include <Windows.h>
#include <ShlObj.h>
#include <Shlwapi.h>
 
#if defined(_MSC_VER)
  #pragma comment(lib, "Shlwapi")
#endif

void func(void) {
  HRESULT hr;
  LPWSTR path = 0;
  WCHAR full_path[MAX_PATH];

  hr = SHGetKnownFolderPath(&FOLDERID_Documents, 0, NULL, &path);
  if (FAILED(hr)) {
    /* Handle error */
  }
  if (!PathCombineW(full_path, path, L".config")) {
    /* Handle error */
  }
  CoTaskMemFree(path);
  if (!DeleteFileW(full_path)) {
    /* Handle error */
  }
}

Exceptions

ENV33-C-EX1: It is permissible to call system() with a null pointer argument to determine the presence of a command processor for the system.

 Risk Assessments

If the command string passed to system(), popen(), or other function that invokes a command processor is not fully sanitized, the risk of exploitation is high. In the worst case scenario, an attacker can execute arbitrary shellcode system commands on the compromised machine with the privileges of the vulnerable process.

Use of the system() function can result in exploitable vulnerabilities:

  1. When passing an unsanitized or improperly sanitized command string originating from a tainted source, or
  2. If a command is specified without a path name and the command processor path name resolution mechanism is accessible to an attacker, or
  3. If a relative path to an executable is specified and control over the current working directory is accessible to an attacker, or
  4. If the specified executable program can be spoofed by an attacker.

Although exceptions to this rule are necessary, they can only be identified on a case-by-case basis during a code review and are consequently outside the scope of this rule.

Recommendation

Rule

Severity

Likelihood

Detectable

Remediation Cost

Repairable

Priority

Level

ENV04

ENV33-C

high

High

Probable

probable

Yes

medium

No

P12

L1

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Compass/ROSE  
Astrée_V
Astrée_V
stdlib-use-systemFully checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-ENV33
Clang
Include Page
Clang_39_V
Clang_39_V
cert-env33-cChecked by clang-tidy
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.PATH.SYSTEM
IO.INJ.COMMAND

Use of system
Command injection

Compass/ROSE


Coverity
Include Page
Coverity_V
Coverity_V
DONT_CALLImplemented
Cppcheck Premium

Include Page
Cppcheck Premium_V
Cppcheck Premium_V

premium-cert-env33-c
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C5018

C++5031

  


Klocwork
Include Page
Klocwork_V
Klocwork_V

SV.CODE_INJECTION.SHELL_EXEC

SV.TAINTED.INJECTION



LDRA tool suite
Include Page
LDRA_V
LDRA_V

588 S

Fully implemented
PRQA QA-C Include PagePRQA_VPRQA_VWarncall -wc systemPartially implemented
Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-ENV33-a

The 'system()' function from the 'stdlib.h' or 'cstdlib' library should not be used with an argument other than '0' (null pointer)

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

586

Fully supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V


CERT C: Rule ENV33-C

Checks for unsafe call to a system function (rule fully covered)

RuleChecker
Include Page
RuleChecker_V
RuleChecker_V
stdlib-use-systemFully checked
SonarQube C/C++ Plugin
Include Page
SonarQube C/C++ Plugin_V
SonarQube C/C++ Plugin_V
S990Detects uses of "abort", "exit", "getenv" and "system" from <stdlib.h> 

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT C Secure Coding StandardENV03-C. Sanitize the environment when invoking external programs.Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C++

...

...

...

Prior to 2018-01-12: CERT: Unspecified Relationship

...

...

...

...

Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC

...

TR 24772:2013Unquoted Search Path or Element [XZQ]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC

...

TS 17961:2013Calling system [syscall]

...

Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11CWE-88,

...

Argument

...

MITRE CWE: CWE-78, "Failure to sanitize data into an OS command (aka "OS command injection")"

Bibliography

...

Injection or Modification2017-05-18: CERT: Partial overlap
CWE 2.11CWE-6762017-05-18: CERT: Rule subset of CWE

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-88 and ENV33-C

Intersection( CWE-88, ENV33-C) =

Allowing an argument to be injected during a call to system()

CWE-88 = Intersection( CWE-88, ENV33-C, list) where list =


  • Allowing an argument to be injected during a call to a command interpreter besides system()


ENV33-C = Intersection( CWE-88, ENV33-C, list) where list =


  • Other exploits to a call to system(), which include:



  • Altering the pathname of the command to invoke (argv[0])



  • Injection of a second command



  • Redirection of standard input, output, or error


CWE-78 and ENV33-C

ENV33-C = Union( CWE-78, list), where list =


  • Invoking system() with completely trusted arguments


CWE-676 and ENV33-C


  • Independent( ENV33-C, CON33-C, STR31-C, EXP33-C, MSC30-C, ERR34-C)



  • ENV33-C forbids calling system().



  • CWE-676 does not indicate what functions are ‘potentially dangerous’; it only addresses strcpy() in its examples. Any C standard library function could be argued to be dangerous, and rebutted by saying that the function is safe when used properly. We will assume that CERT rules mapped to CWE-676 specify dangerous functions. So:



  • CWE-676 = Union( ENV33-C, list) where list =



  • Invocation of other dangerous functions, besides system().


Bibliography

[IEEE Std 1003.1:2013]XSH, System Interfaces, exec
XSH, System Interfaces, popen
XSH, System Interfaces, unlink
[Wheeler 2004]


...

Image Modified Image Modified Image Modified