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])