Guidelines

Command Injection

Let’s look at Command Injection by itself for now. We’re mostly going to focus on a few different examples so it’s easier to see what it looks like in action. So, as a quick refresher, Command Injection vulnerabilities occur when user input uses part of an operating system command, like the following function:

let ip = request.params.ipAddress;

system("ping " + ip);

If a user provides the IP address, we’ll use `8.8.8.8` as an example, the command that gets executed will be `ping 8.8.8.8`, which does exactly what one would expect. However,  if the user provides `8.8.8.8 && ls /etc/` this command wouldn’t just ping the IP 8.8.8.8, but it’ll also run `ls` on the `/etc/ folder. 

Mitigation

Given the severity of a command injection attack, there are some important questions you need to ask first when working with system commands:

  • Do you really need to invoke that command? The best defense is to not ever invoke system commands
  • Are there any libraries/bindings that allow you to achieve the same effect without the use of a system command?
  • Can you pass data into the process through Standard In, instead of the command itself? 

If these things aren’t possible, parameterization is important.

Examples

Here are a few examples in various languages to help show how this looks in practice. 

Without the use of parameterization, this is vulnerable to Command Injection.

string folder = "/tmp/ && ifconfig";
string cmd =  "\"ls " + folder +"\"";

// INSECURE: Executes both the `ls` and `ifconfig` command 
System.Diagnostics.Process.Start("bash", "-c " + cmd);

C# - secure

By providing the command as a list of parameters, the command is parameterized and protected against Command Injection.

string folder = "/tmp/ && ifconfig";
List<string> arguments = new List<string>() {"-c", "ls", folder}; 

// SECURE: Does not execute ifconfig command
System.Diagnostics.Process.Start("bash", arguments);

Java - Insecure

Without the use of parameterization, this is vulnerable to Command Injection.

String folder = "/tmp/ && ifconfig";

// INSECURE: Executes both the `ls` and `ifconfig` command 
ProcessBuilder b = new ProcessBuilder("bash -c ls " + folder);

Process p = pb.start();

Java - Secure

By providing the command as a list of parameters, the command is parameterized and protected against Command Injection.

String folder = "/tmp/ && ifconfig";

// SECURE: Does not execute ifconfig command
ProcessBuilder b = new ProcessBuilder("bash", "-c", "ls", folder);

Process p = pb.start();

Javascript - Insecure

Without the use of parameterization, this is vulnerable to Command Injection.

const { exec } = require("child_process");

const folder = "/tmp/ && ifconfig";

// INSECURE: Executes both the `ls` and `ifconfig` command 
const ls = exec("bash -c ls " + folder, (error, stdout, stderr) => {
     console.log(`stdout: ${stdout}`);
});

Javascript - Secure

const { spawn } = require("child_process");

const folder = "/tmp/ && ifconfig";

// SECURE: Does not execute ifconfig command
const ls = spawn("bash", ["-c", "ls", folder]);

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

Python - Insecure

Without the use of parameterization, this is vulnerable to Command Injection.

import subprocess

folder = "/tmp/ && ifconfig"

# INSECURE: Executes both the `ls` and `ifconfig` command 
subprocess.run("bash -c ls " + folder, shell=True)

Python - Secure

By providing the command as a list of parameters, the command is parameterized and protected against Command Injection.

import subprocess

folder = "/tmp/ && ifconfig"

# SECURE: Does not execute ifconfig command
subprocess.run(["bash", "-c", "ls", folder])