Main Content

CWE Rule 114

Process Control

Since R2024a

Description

Rule Description

Executing commands or loading libraries from an untrusted source or in an untrusted environment can cause an application to execute malicious commands (and payloads) on behalf of an attacker.

Polyspace Implementation

The rule checker checks for these issues:

  • Command executed from externally controlled path

  • Execution of a binary from a relative path can be controlled by an external actor

  • Execution of externally controlled command

  • Library loaded from externally controlled path

  • Load of library from a relative path can be controlled by an external actor

Examples

expand all

Issue

This issue occurs when the path to a command executed in the program is constructed from external sources.

Risk

An attacker can:

  • Change the command that the program executes, possibly to a command that only the attack can control.

  • Change the environment in which the command executes, by which the attacker controls what the command means and does.

Fix

Before calling the command, validate the path to make sure that it is the intended location.

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 — Executing Path from Environment Variable
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

void bug_taintedpathcmd() {
    char cmd[SIZE128] = "";
    char* userpath = getenv("MYAPP_PATH");

    strncpy(cmd, userpath, SIZE100);
    strcat(cmd, "/ls *");
    /* Launching command */
    system(cmd);//Noncompliant  //Noncompliant
}

This example obtains a path from an environment variable MYAPP_PATH. The path string is tainted. Polyspace flags its use in the strncopy function. system runs a command from the tainted path without checking the value of the path. If the path is not the intended path, your program executes in the wrong location.

Correction — Use Trusted Path

One possible correction is to use a list of allowed paths to match against the environment variable path.

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

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

/* Function to sanitize a string */
/* Any defect is localized here */
int sanitize_str(char* s, size_t n) {
    int res = 0;
    /* String is ok if */
    if (s && n>0 && n<SIZE128) {
        /* - string is not null                     */
        /* - string has a positive and limited size */
        s[n-1] = '\0';  /* Add a security \0 char at end of string */ // Compliant
        /* Tainted pointer detected above, used as "firewall" */
        res = 1;
    }
    return res;
}

/* Authorized path ids */
enum { PATH0=1, PATH1, PATH2 };

void taintedpathcmd() {
    char cmd[SIZE128] = "";

    char* userpathid = getenv("MYAPP_PATH_ID");
    if (sanitize_str(userpathid, SIZE100)) {
        int pathid = atoi(userpathid);


        char path[SIZE128] = "";
        switch(pathid) {
            case PATH0:
                strcpy(path, "/usr/local/my_app0");
                break;
            case PATH1:
                strcpy(path, "/usr/local/my_app1");
                break;
            case PATH2:
                strcpy(path, "/usr/local/my_app2");
                break;
            default:
                /* do nothing */
		break;
        }
        if (strlen(path)>0) {
            strncpy(cmd, path, SIZE100);
            strcat(cmd, "/ls *");
            system(cmd);    
        }
    }
}
Issue

This issue occurs when you call an external command with a relative path or without a path.

This defect also finds results that the Execution of externally controlled command defect checker finds.

Risk

By using a relative path or no path to call an external command, your program uses an unsafe search process to find the command. An attacker can control the search process and replace the intended command with a command of their own.

Fix

When you call an external command, specify the full path.

Example — Call Command with Relative Path
# define _GNU_SOURCE
# include <sys/types.h>
# include <sys/socket.h>
# include <unistd.h>
# include <stdio.h>
# include <stdlib.h>
# include <wchar.h>
# include <string.h>
# define MAX_BUFFER 100

void rel_path()
{
    char * data;
    char data_buf[MAX_BUFFER] = "";
    data = data_buf;

    strcpy(data, "ls -la");
    FILE *pipe;
    pipe = popen(data, "wb");  //Noncompliant
    if (pipe != NULL) pclose(pipe);
}

In this example, Bug Finder flags popen because it tries to call ls -la using a relative path to the ls command. An attacker can manipulate the command to use a malicious version.

Correction — Use Full Path

One possible correction is to use the full path when calling the command.

# define _GNU_SOURCE
# include <sys/types.h>
# include <sys/socket.h>
# include <unistd.h>
# include <stdio.h>
# include <stdlib.h>
# include <wchar.h>
# include <string.h>
# define MAX_BUFFER 100

void rel_path()
{
    char * data;
    char data_buf[MAX_BUFFER] = "";
    data = data_buf;

    strcpy(data, "/usr/bin/ls -la");
    FILE *pipe;
    pipe = popen(data, "wb");  
    if (pipe != NULL) pclose(pipe);
}
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  //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.

#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 cmd[SIZE128] = "/usr/bin/cat ";

    switch(usercmd) {
        case CMD0:
            strcat(cmd, "*.c");
            break;
        case CMD1:
            strcat(cmd, "*.h");
            break;
        case CMD2:
            strcat(cmd, "*.cpp");
            break;
        default:
            strcat(cmd, "*.c");
    }
    system(cmd); 
}
Issue

This issue occurs when libraries are loaded from fixed or externally controlled unsecure paths and unintended actors can control one or more locations on the paths.

Risk

If an attacker knows or controls the path that you use to load a library, the attacker can change:

  • The library that the program loads, replacing the intended library and commands.

  • The environment in which the library executes, giving unintended permissions and capabilities to the attacker.

Fix

When possible, use hard-coded or fully qualified path names to load libraries. It is possible the hard-coded paths do not work on other systems. Use a centralized location for hard-coded paths, so that you can easily modify the path within the source code.

Another solution is to use functions that require explicit paths. For example, system() does not require a full path because it can use the PATH environment variable. However, execl() and execv() do require the full path.

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 Custom Library
#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
};

void* taintedpathlib() {
    void* libhandle = NULL;
    char lib[SIZE128] = "";
    char* userpath = getenv("LD_LIBRARY_PATH");
    strncpy(lib, userpath, SIZE128);// Userpath is tainted
    strcat(lib, "/libX.so");
    libhandle = dlopen(lib, 0x00001); //Noncompliant
    return libhandle;
}

This example loads the library libX.so from an environment variable LD_LIBRARY_PATH. An attacker can change the library path in this environment variable. The actual library you load could be a different library from the one that you intend.

Correction — Change and Check Path

One possible correction is to change how you get the library path and check the path of the library before opening the library. This example receives the path as an input argument but then performs the following checks on the path:

  • The function sanitize_str protects against possible buffer overflows.

  • The function identified_safe_libX_folder checks if the path belongs to a list of allowed paths.

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

/* Use allowlist */
static const char *libX_safe_folder[] = {
  "/usr/",
  "/usr/lib",
  "/lib"
};

/* Return the index if the input is in the allowlist */
int identified_safe_libX_folder(const char* path)
{
  for (int i = 0; i < sizeof(libX_safe_folder) / sizeof(libX_safe_folder[0]); i ++)
    {
      if (strcmp(path, libX_safe_folder[i]) == 0)
      return i;
    }
  return -1;    
}

/* Function to sanitize a string */
char *sanitize_str(char* s, size_t n) {
  /* strlen is used here as a kind of firewall for tainted string errors */
  if (strlen(s) > 0 && strlen(s) < n)
    return s;
  else
    return NULL;
}

void* taintedpathlib(char* userpath) {
  void* libhandle = NULL;
  const char *const checked_userpath = sanitize_str(userpath, SIZE128);
  if (checked_userpath != NULL) {
    int index = identified_safe_libX_folder(checked_userpath);
    if (index > 0) {
      char lib[SIZE128] = "";
      strncpy(lib, libX_safe_folder[index], SIZE128);
      strcat(lib, "/libX.so");
      libhandle = dlopen(lib, RTLD_LAZY);    
    }
  }
  return libhandle;
}

Issue

This issue occurs when library loading routines that load an external library use a relative path or do not use a path at all.

Risk

By using a relative path or no path to load an external library, your program uses an unsafe search process to find the library. An attacker can control the search process and replace the intended library with a library of their own.

Fix

When you load an external library, specify the full path.

Example — Open Library with Library Name
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>

void relative_path()
{
    dlopen("liberty.dll",RTLD_LAZY); //Noncompliant
}

In this example, dlopen opens the liberty library by calling only the name of the library. However, this call to the library uses a relative path to find the library, which is unsafe.

Correction — Use Full Path to Library

One possible correction is to use the full path to the library when you load it into your program.

#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>

void relative_path()
{
    dlopen("/home/my_libs/library/liberty.dll",RTLD_LAZY);
}

Check Information

Category: Others

Version History

Introduced in R2024a