Protecting Against Command Execution Attacks

If an attacker can execute arbitrary code on your servers, your systems are almost certainly going to be compromised. You need to take great care when designing how your web server interacts with the underlying operating system.


Prevalence Common
Exploitability Moderate
Impact Devastating

Command injection is a major security lapse, and the last step along the road to complete system takeover. After gaining access, an attacker will attempt to escalate their privileges on the server, install malicious scripts, or make your server part of a botnet to be used at a later date.

Command injection vulnerabilities often occur in older, legacy code, such as CGI scripts.


If your application calls out to the operating system, you need to be sure command strings are securely constructed, or else you risk having malicious instructions injected by an attacker. This section outlines a few approaches to protecting yourself.

Try to Avoid Command Line Calls Altogether

Modern programming languages have interfaces that permit you to read files, send emails, and perform other operating system functions. Use APIs wherever possible – only use shell commands where absolutely necessary. This will reduce the number of attack vectors in your application, and will also simplify your codebase.

Escape Inputs Correctly

Injection vulnerabilities occur when untrusted input is not sanitized correctly. If you use shell commands, be sure to scrub input values for potentially malicious characters:


Even better, restrict input by testing it against a regular expression of known safe characters. (For example, alphanumeric characters.)

Restrict the Permitted Commands

Try to construct all or most of your shell commands using string literals, rather than user input. Where user input is required, try to allowlist permitted values, or enumerate them in a conditional statement.

Perform Thorough Code Reviews

Check system calls for vulnerabilities as a part of your code review process. Vulnerabilities often creep in over time – make sure your team knows what to look for.

Run with Restricted Permissions

It is a good practice to run your server processes with only the permissions that they require to function – the principle of least privilege. This can help limit the impact of command injection vulnerabilities as a second line of defense.

Make sure each web server process can only access the directories that it needs, and narrow down the directories in which they write or execute files. Consider running the process in a chroot jail if you are running on Unix. This will limit the ability of maliciously injected code to “climb out” of a directory.

Code Samples

The code samples below illustrate how to safely make command-line calls in various languages.

New processes are spawned in python using the modules popen2, os, commands, and subprocess. subprocess is the preferred API (the others are deprecated and replaced by it). The subprocess module has built-in protection against command execution:

from subprocess import call

# An invocation of the call(...) function will ensure only a single
# command is run.
call(["ls", "-l"])

This protection can be disabled – be on the lookout for anything that opens a process in the following manner:

from subprocess import call

# shell=True disables command injection checking.
call("cat " + filename, shell=True)

Ruby offers a multitude of ways to make command line calls:

eval "ls -l"

system "ls -l"

`ls -l` # Backticks indicate an OS command to be executed.

Kernel.exec("ls -l")

%x( ls -l )

open("|date") do |cmd|
   print cmd.gets

If you must use command line calls in your application, be sure to sanitize inputs using the Shellwords module:

open("| grep #{Shellwords.escape(pattern)} file") { |pipe|
  # ...

Since Java is run in a virtual machine, command-line calls are generally discouraged. They can be executed using the java.lang.Runtime API, however:

Runtime.getRuntime().exec("ls -l");

The call to exec(...) will tokenize the input and make sure only a single command is run. Just be sure not to write your own tokenizer!

There are a couple of ways to access the command line in C#, though .NET has a very comprehensive set of standard libraries, so you will rarely need to:

System.Diagnostics.Process.Start("CMD.exe", "ls -l");

var process = new ProcessStartInfo();
process.UseShellExecute = true;
process.WorkingDirectory = @"C:\Windows\System32";
process.FileName = @"C:\Windows\System32\cmd.exe";
process.Verb = "runas";
process.Arguments = "/c ls -l";

The usual way of invoking shell commands in Node is using the child_process module. This is a wrapper around, so concatenating command strings is risky:

var child_process = require('child_process');

child_process.exec('ls -l ' + input_path, function (err, data) {

Instead, use one of the functions that take arrays as arguments:

child_process.execFile('/bin/ls', ['-l', input_path], function (err, result) {
var ls = child_process.spawn('/bin/ls', ['-l', input_path])

Making command-line calls in PHP is fairly common, and there are a number of ways to make them:

shell_exec "ls -l"

exec "ls -l"

passthru "ls -l"

system "ls -l"

`ls -l`

Be careful to sanitize the inputs to any of these functions.

Further Reading