Learn how to remediate exec-use findings in your PHP code by Semgrep.

How to remediate Semgrep PHP Rule exec-use

Semgrep's exec-use rule is triggered when you handle "improperly" the following PHP functions to execute external commands in the machine:

  • exec
  • passthru
  • proc_open
  • popen
  • shell_exec
  • system
  • pcntl_exec

If a product permits users to input code syntax, it creates a potential vulnerability where an attacker can manipulate the code to disrupt the intended control flow of the product. This manipulation has the potential to result in the execution of arbitrary code. You can statically analyze PHP code in Semgrep's sandbox online here, so you can experiment and determine how your code presents the exec-use flaw.

Identifying how the exec-use rule works

It's very important to understand, how Semgrep identifies this flaw in your code before proceeding with a solution.

When it's safe according to Semgrep

There are a few cases when using the PHP's exec function is accepted by Semgrep:

  • When the command is hard coded in any way that cannot be modified by some logic.
  • When Semgrep can determine that a variable is not being modified by any other function or there's no context from where the variable is coming from.
<?php

// exec-use: ok
// the command is hardcoded and cannot be modified by some logic or user input
exec("ping ourcodeworld.com");

// exec-use: ok
// The command, although stored in a variable, is not modified by any logic or user input
$command = "ping ourcodeworld.com";
exec($command);

When it's unsafe according to Semgrep

There are many ways in which Semgrep can mark the use of exec as unsafe:

<?php

// exec-use: unsafe
// Because the command is not validated 
// and semgrep is unable to determine if the command is safe or not
// because the command comes from a function that may have changed it
// This definitely requires your attention in some cases.
function buildCommand(){
    return "ping ourcodeworld.com";
}

exec(buildCommand());

// exec-use: unsafe
// This is unsafe because the user can pass any domain
// $domain is not fixed, so this could lead to command injection
// Interpolation of variables in a string will trigger as well the rule
$domain = $_GET['domain'];
exec("ping $domain");

// exec-use: unsafe
// Even though the domain is escaped, it is still unsafe
// as it's user input
$domain = $_GET['domain'];
exec("ping " . escapeshellarg($domain));

According to the common weakness enumeration standard, the exec-use rule refers to the rule CWE-94 Improper Control of Generation of Code ('Code Injection').

Exceptions you can make

As with every static analysis tool, there might be some cases that you can mark as a false positive. Check the first example that we wrote:

<?php

function buildCommand(){
    return "ping ourcodeworld.com";
}

exec(buildCommand());

This case is marked as unsafe, however, if you think about it, in the theory is safe because the command is indeed hardcoded, is just that it's returned by a function, so in this case, the solution to prevent this piece from appearing in the Semgrep report would be to mark it as an exception:

<?php

function buildCommand(){
    return "ping ourcodeworld.com";
}

// nosemgrep: exec-use
exec(buildCommand());

It's very important that you limit the input somehow so the user won't be the one who provides it.

Tips to remediate this issue

Note that all of them are contextual to what your project does and what it needs, so if considering the logic of the project, you think that the tip could be applied, don't hesitate to follow it. For example, do not disable the dangerous functions (such as exec) if your project's main feature relies on running a binary on the server side to do something. Analyze the tips and use what could be useful in your case.

1. Run only trusted commands

This tip refers to using exec only with trusted commands from reliable sources. You need to avoid any user-supplied input directly to the function, as this can potentially lead to a command injection vulnerability.

2. Run exec with the least privileges approach

Do not ever run scripts that use exec with root/administrator privileges to limit the potential impact if a vulnerability is ever exploited.

3. Disable dangerous functions

If you have control over the PHP settings, consider disabling the usage of potentially dangerous functions such as those mentioned in this article.

4. Log and handle errors

When using these functions, try to implement a proper logging and error-handling mechanism to track and handle any error or unexpected behavior that may happen when running commands through those functions.

5. Limit the set of commands

Instead of allowing the code to run any arbitrary command in your application with something like this:

<?php

$executable = $_GET['executable'];
$argument = $_GET['argument'];
$output = array();

// Dangerous!!!!!!
exec($executable . ' ' . $argument, $output);

Try to limit the executable commands somehow, either by using a whitelist of allowed commands or configuration settings:

<?php

$executable = $_GET['executable'];
$argument = $_GET['argument'];
$output = array();

// Will still trigger semgrep rule, but will not be exploitable
// If someone sends a command that is not ping or traceroute, it will not be executed
// So you can exclude those lines from the report
switch($executable){
    case "ping":
        exec("ping " . escapeshellarg($argument), $output);
        break;
    case "traceroute":
        exec("traceroute " . escapeshellarg($argument), $output);
        break;
}

6. Escape shell arguments

When incorporating user input into the command, which is something dangerous, it is essential to escape the input properly to prevent any character from being interpreted as a shell metacharacter. For PHP you can use the following functions to sanitize your input:

  1. escapeshellcmd: is used when the entire command is provided as a plain string.
  2. escapeshellarg: used when passing arguments to your command.

The escapeshellarg function can be used like this:

<?php

$arg = escapeshellarg($_GET['argument']);
$command = "ping " . $arg;
exec($command);

And the escapeshellcmd function can be used like this:

<?php

$path = $_GET['argument'];
exec(escapeshellcmd('ls '.$path))

As a final note, it's very important to note that exec and other kinds of functions that allow users to run commands can still be a potential security risk, even when used with caution, so whenever it is possible, explore alternative approaches that do not involve executing external commands directly from PHP.


Senior Software Engineer at Software Medico. Interested in programming since he was 14 years old, Carlos is a self-taught programmer and founder and author of most of the articles at Our Code World.

Sponsors