Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added Windows examples; reviewed

...

Code Block
bgColor#ccccff
languagec
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
 
void func(const 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_exe", args, env) == -1) {
      /* Handle error */
      _Exit(127);
    }
  }
}

...

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"), input, NULL, NULL, FALSE,
                     0, 0, 0, &si, &pi)) {
    /* Handle error */
  }
  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
}

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

...

Be careful using unlink(), particularly when running with elevated privileges, because it may be susceptible to file-related race conditions. (See FIO01-C. Be careful using functions that use file names for identification.)

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 */
  }
}

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 on the compromised machine with the privileges of the vulnerable process.

...