Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Use of the system() function can result in exploitable vulnerabilities, in the worst case allowing execution of arbitrary shell codesystem commands. Situations where 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

...

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
#include <string.h>
#include <stdlib.h>
 

enum { BUFFERSIZE = 512 };

void func(const char *input) {
  enum { BUFFERSIZE = 512 };
  char cmdbuf[BUFFERSIZE];
  int len_wanted = snprintf(cmdbuf, BUFFERSIZE,
                            "any_cmd '%s'", input);
  if (len_wanted >= BUFFERSIZE) {
    /* Handle error */
  } else if (len_wanted < 0) {
    /* Handle error */
  } else if (system(cmdbuf) == -1) {
    /* Handle error */
  }
}

...

The shell would interpret this string as two separate commands:,

Code Block
any_cmd 'happy';
useradd 'attacker'

...

In this compliant solution, the call to system() is replaced with a call to execve(). The exec family of 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.

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 be used without a forward slash character (/) only if the PATH environment variable 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 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
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.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) {
    /* Handle error */
  } else if (pid != 0) {
    while ((ret = waitpid(pid, &status, 0)) == -1) {
      if (errno != EINTR) {
        /* Handle error */
        break;
      }
    }
    if ((ret != -1) &&
      (!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) {
      /* Handle error */
      _Exit(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(), eliminating any 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. Although this method is more complicated than calling system(), the added security is worth the additional effort.

...

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

The This compliant solution is relying relies on the fact that the input parameter is being non-const. If it were const, it the solution would be required 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.

...

If the vulnerable program has 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 invoking the system() is to use the POSIX unlink() function[IEEE Std 1003.1:2013]:

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

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

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

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

  free(file);
}

Be careful using The unlink(), particularly when running with elevated privileges, because it may be function is not susceptible to file-related race conditions . (See see FIO01-C. Be careful using functions that use file names for identification.)) because if file names a symbolic link, unlink() will remove the symbolic link named by file and not affect any file or directory named by the contents of the symbolic link.

Compliant Solution (Windows)

This compliant solution uses the Microsoft Windows SHGetKnownFolderPath() API to get the current user's My Documents folderDocuments 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.

...

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.

...

CERT C Secure Coding StandardENV03-C. Sanitize the environment when invoking external programs.
CERT C++ Secure Coding StandardENV04-CPP. Do not call system() if you do not need a command processor
CERT Oracle Secure Coding Standard for JavaIDS07-J. Do not pass untrusted, unsanitized data to the Runtime.exec() method
ISO/IEC TR 24772:2013Unquoted Search Path or Element [XZQ]
ISO/IEC TS 17961:2013Calling system [syscall]
MITRE CWECWE-78, Failure to sanitize data into Improper Neutralization of Special Elements Used in an OS command Command (aka "OS command injectionCommand Injection")
CWE-88, Argument injection Injection or modificationModification

Bibliography

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

...