# Command Injection

## Overview

Command injection (or OS command injection) is an attack in which the goal is the execution of arbitrary OS commands. Command injection attacks are possible when an application passes user-controlled data directly to the shell, allowing an attacker to inject and execute arbitrary commands.

For example, in the snippet below, if an attacker can control the `ip` variable, they will be able to execute arbitrary commands by passing the `1.1.1.1; id` value to `ip` variable.

```python
os.system(f'ping {ip}')
# if ip == '1.1.1.1; id' the shell command will be:
# ping 1.1.1.1; id
```

You can find more details at [PortSwigger Web Security Academy: OS command injection](https://portswigger.net/web-security/os-command-injection).

This page contains recommendations for the implementation of protection against command injection attacks.

## General

<div align="left"><img src="https://1795604890-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaH8j4W1MtabOUlUc8Trn%2Fuploads%2Fgit-blob-b41291c03c4de901e1f0faa235c5ad68838b2947%2Ftype-base-icon.svg?alt=media" alt=""></div>

* Do **not** call OS commands directly if there is a library or API for your language.

<details>

<summary>Clarification</summary>

It is better to use a built-in method to perform the required task than to call an OS command.

For example, use `mkdir(name)` instead of `system('mkdir ${name}')`.

</details>

* Use parameterized methods that allow automatically applying a separation between data and command, see the [Parameterized command execution implementation](#parameterized-command-execution-implementation) section.
  * Usually parameterized methods take a list as input (a command name and its arguments), **not** a string. So, if a method accepts a string, this method is most likely vulnerable to command injection.
* If it is not possible to use APIs or parameterized methods, you can use environment variables to pass user-controlled data.
* Do **not** use user-controlled data as a name when setting environment variables. Instead, generate random names.

<details>

<summary>Clarification</summary>

Environment variables can be used to inject commands and execute arbitrary code. You can find a list of dangerous environment variables at [Application Security Cheat Sheet: Command Injection](https://0xn3va.gitbook.io/cheat-sheets/web-application/command-injection).

</details>

* Implement comprehensive input validation, see the [Input Validation](https://0xn3va.gitbook.io/application-security-handbook/web-application/input-validation) page.
  * Define an allow list of commands.
  * Define an allow list of arguments.
  * Define an allow list of characters (alphanumeric characters only) to validate commands, arguments and operands.
  * Define an allow list of environment variable names.
  * Define an allow list of environment variable values.
  * Do **not** use block list validation.
* Do **not** run commands inside of a shell.
  * Do **not** pass a shell to a parameterized method as a command. For example, the following snippet is vulnerable to command injection:

    ```javascript
    // vulnerable to command injection
    system(['/bin/sh', '-c', user_input])
    ```
  * Do **not** enable running commands inside of a shell using an argument like [the "shell" in child\_process.Spawn in Node.js](https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options):

    ```javascript
    const { spawn } = require('node:child_process');
    // command execution when shell is set to false
    const cmd = spawn('cmd', ['arg1', 'arg2']);
    // command execution when shell is set to true
    const cmd = spawn('cmd arg1 arg2', { shell: true });
    ```
* Use `--` to separate operands from arguments.

<details>

<summary>Clarification</summary>

Unfortunately, using parameterized functions for command execution does not protect from the argument injection attack. Depending on the command used, argument injection can provide an attacker with additional leverage, such as a primitive for writing arbitrary files or even executing arbitrary code.

You can find arguments for various commands that can be abused at [Application Security Cheat Sheet: Command Injection - Argument Injection](https://0xn3va.gitbook.io/cheat-sheets/web-application/command-injection/argument-injection).

`--` allows you to explicitly separate operands from arguments. For example, the code in the snippet below takes the data from `user_input` as an argument and the OS command will print the listing of the the current folder.

```javascript
user_input = '-la'
system('ls', user_input)
// => ls -la
```

However, using `--` can help avoid this issue. The code in the snippet below will return an error because `-la` will be treated as a name, not an argument.

```javascript
user_input = '-la'
system('ls', '--', user_input)
// => ls -- -la
// ls: -la: No such file or directory
```

</details>

* Be aware that methods not directly related to executing OS commands can execute OS commands under the hood and be vulnerable to command injection.

<details>

<summary>Clarification</summary>

For example, `Kernel.open` is used to open files in Ruby but can be used to execute OS commands:

```ruby
open("| echo 'echo from open'").read()
# => 'echo from open\n
```

You can find a list of dangerous methods at [Application Security Cheat Sheet: Command Injection](https://0xn3va.gitbook.io/cheat-sheets/web-application/command-injection).

</details>

<div align="left"><img src="https://1795604890-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaH8j4W1MtabOUlUc8Trn%2Fuploads%2Fgit-blob-4b891edb8f5a26f439d757f90613f83f54c2107c%2Ftype-advanced-icon.svg?alt=media" alt=""></div>

* Use a sandbox to execute OS commands.

## Parameterized command execution implementation

{% tabs %}
{% tab title="Go" %}
Use the [os/exec](https://pkg.go.dev/os/exec) package to execute OS commands.

```go
package main

import (
    "fmt"
    "os/exec"
)

func main() {
    cmd := exec.Command("cmd", "arg1", "arg2")
    out, _ := cmd.Output()
    fmt.Printf("%s", out)
}
```

{% endtab %}

{% tab title="Java" %}
Use the [Runtime.exec(String\[\] cmdarray)](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html) method or the [ProcessBuilder.command(List command)](https://docs.oracle.com/javase/8/docs/api/java/lang/ProcessBuilder.html) method to execute OS commands.

```java
import java.io.*;

public class ParameterizedCommandExecution {

    public static void main(String []args){
        try {
            String commandArray[] = {"cmd", "arg1", "arg2"};
            Process process = Runtime.getRuntime().exec(commandArray);
            try (BufferedReader stdoutReader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = stdoutReader.readLine()) != null) {
                    System.out.println(line);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

{% endtab %}

{% tab title="Node.js" %}
Use the [child\_process.spawn](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options) or [child\_process.spawnSync](https://nodejs.org/api/child_process.html#child_processspawnsynccommand-args-options) function to execute OS commands.

Do **not** set the `shell` option to `true`. It will disable the parameterization.

```javascript
const { spawn } = require('node:child_process');
const cmd = spawn('cmd', ['arg1', 'arg2']);

cmd.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

cmd.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

cmd.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});
```

{% endtab %}

{% tab title="Python" %}
Use the [subprocess](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options) module to execute OS commands.

Do **not** set the `shell` option to `True`. It will disable the parameterization.

```python
import subprocess

process = subprocess.run(
    ['cmd', 'arg1', 'arg2'],
    stdout=subprocess.PIPE
)

print(process.stdout.decode('utf-8'))
```

{% endtab %}
{% endtabs %}

## References

* [OWASP Cheat Sheet Series: OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)
