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.

Risks

Prevalence
Common
Rating prevelance on a298cccc3e525887223509d0e6fe9a464d7d7f60574014de1fe402608154d354 Rating prevelance on a298cccc3e525887223509d0e6fe9a464d7d7f60574014de1fe402608154d354 Rating prevelance on a298cccc3e525887223509d0e6fe9a464d7d7f60574014de1fe402608154d354
Exploitability
Moderate
Rating exploitability on 6b817c6c589f0911378579408b6cbfc6d82345849ae2da559b8d11602b9a987b Rating exploitability on 6b817c6c589f0911378579408b6cbfc6d82345849ae2da559b8d11602b9a987b Rating exploitability on 6b817c6c589f0911378579408b6cbfc6d82345849ae2da559b8d11602b9a987b
Impact
Devastating
Rating impact on 48bdb4077813afe9762f27e229e64207ec59c3891a54a3adf931c2c91a6d99bd Rating impact on 48bdb4077813afe9762f27e229e64207ec59c3891a54a3adf931c2c91a6d99bd Rating impact on 48bdb4077813afe9762f27e229e64207ec59c3891a54a3adf931c2c91a6d99bd

Remote code execution 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.

Protection

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 operation 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 whitelist 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
   end

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";
   Process.Start(process);

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


   var child_process = require('child_process');

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

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


   child_process.execFile('/bin/ls', ['-l', input_path], function (err, result) {
     console.log(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

A note about shell commands in Python.

A guide to shell commands in Ruby.

Command injection in Ruby.

Command injection in Java.

Command injection in Node.

Is your website vulnerable to command execution attacks?
Netsparker n 834848961a0bf6ec5556448ff47f421d0b1204a572877a59717064b1088e8c43
Check today. Scan your website for Command Execution and other vulnerabilities with Netsparker Web Application Security Scanner