👨‍💻
Application Security Handbook
  • Application Security Handbook
  • Web Application
    • Authentication
      • Authentication with Login and Password
      • Authentication with Phone Number
      • OAuth 2.0 Authentication
      • Multi-factor Authentication
      • Default Passwords
      • Password Change
      • Password Policy
      • Password Reset
      • Password Storage
      • One Time Password (OTP)
      • Email Address Confirmation
    • Authorization
    • Concept of Trusted Devices
    • Content Security Policy (CSP)
    • Cookie Security
    • Cryptography
      • Cryptographic Keys Management
      • Encryption
      • Hash-based Message Authentication Code (HMAC)
      • Hashing
      • Random Generators
      • Universal Unique Identifier (UUID)
    • Error and Exception Handling
    • File Upload
    • Input Validation
    • JSON Web Token (JWT)
    • Logging and Monitoring
    • Output Encoding
    • Regular Expressions
    • Sensitive Data Management
    • Session Management
    • Transport Layer Protection
    • Vulnerability Mitigation
      • Brute-force
      • Command Injection
      • Cross-Site Request Forgery (CSRF)
      • Cross-Site Scripting (XSS)
      • Mass Parameter Assignment
      • Parameter Pollution
      • Path Traversal
      • Regular Expression Denial of Service (ReDoS)
      • SQL Injection (SQLi)
      • XML External Entity (XXE) Injection
Powered by GitBook
On this page
  • Overview
  • General
  • Parameterized command execution implementation
  • References
  1. Web Application
  2. Vulnerability Mitigation

Command Injection

PreviousBrute-forceNextCross-Site Request Forgery (CSRF)

Last updated 1 year ago

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.

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 .

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

General

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

Clarification

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}').

    • 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.

Clarification
    • 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:

      // vulnerable to command injection
      system(['/bin/sh', '-c', user_input])
    • 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.

Clarification

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.

-- 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.

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.

user_input = '-la'
system('ls', '--', user_input)
// => ls -- -la
// ls: -la: No such file or directory
  • Be aware that methods not directly related to executing OS commands can execute OS commands under the hood and be vulnerable to command injection.

Clarification

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

open("| echo 'echo from open'").read()
# => 'echo from open\n
  • Use a sandbox to execute OS commands.

Parameterized command execution implementation

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    cmd := exec.Command("cmd", "arg1", "arg2")
    out, _ := cmd.Output()
    fmt.Printf("%s", out)
}
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();
        }
    }
}

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

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

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

import subprocess

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

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

References

Use parameterized methods that allow automatically applying a separation between data and command, see the section.

Environment variables can be used to inject commands and execute arbitrary code. You can find a list of dangerous environment variables at .

Implement comprehensive input validation, see the page.

Do not enable running commands inside of a shell using an argument like :

You can find arguments for various commands that can be abused at .

You can find a list of dangerous methods at .

Use the package to execute OS commands.

Use the method or the method to execute OS commands.

Use the or function to execute OS commands.

Use the module to execute OS commands.

Application Security Cheat Sheet: Command Injection
Input Validation
the "shell" in child_process.Spawn in Node.js
Application Security Cheat Sheet: Command Injection - Argument Injection
Application Security Cheat Sheet: Command Injection
os/exec
Runtime.exec(String[] cmdarray)
ProcessBuilder.command(List command)
child_process.spawn
child_process.spawnSync
subprocess
OWASP Cheat Sheet Series: OS Command Injection Defense Cheat Sheet
Parameterized command execution implementation
PortSwigger Web Security Academy: OS command injection