What is
Command Injection

stackhawk

StackHawk|April 25, 2022

This article is for developers, testers, and users to understand, detect, and avoid command injection vulnerabilities in their applications.

In software development, security is paramount, but developers tend to forget to test their applications for vulnerabilities. One such vulnerability is command injection. This blog post aims to help developers, testers, and users understand, detect, and avoid command injection vulnerabilities in their applications. 

What Is Command Injection?

Command injection is a cyber attack wherein an attacker takes control of the host operating system by injecting code into a vulnerable application through a command. This code is executed regardless of any security mechanism and can be used to steal data, crash systems, damage databases, and even install malware that can be used later. 

Attackers can access a target system through command injection by using various methods and techniques. The attacker runs arbitrary commands in the system shell of the web server that can compromise all relevant data.  

Next let's look at how it differs from another widespread attack: code injection. 

How Is Command Injection Different from Code Injection?

Code injection is an attack that includes the injection of code executed by an application. This usually involves the attacker sending the target application a request (often through a browser) that includes the injection code. A lack of sufficient input/output data validation allows it to happen.  

A command injection occurs when an attacker alters the application's default function for executing system commands.

No new code is added. Command injection can lead to various breaches, such as downloading tools, stealing and changing credentials, or deleting files that depend on the privileges.

Vulnerabilities That Can Lead to Command Injection

Command injection occurs when an application's vulnerability allows an attacker to extend the application's default functionality by executing system commands. However, executing commands through the application's code is also possible. This is also called code execution. In this case, the goal is to run arbitrary commands through it. 

Now let's look at some of the flaws that might lead to command execution via command injection or code execution.  

1. Arbitrary Command Injection

Arbitrary command injection occurs when an application is vulnerable to a malicious command entered by a user that has the potential to execute any command directly on the underlying host. The attacker may be able to gain access to sensitive data using this type of attack. 

2. Arbitrary File Uploads

When users are allowed to upload files with arbitrary file extensions, command injection can occur when these files are stored in the web root.  

3. Server-Side Template Injection

Server-side template injection is possible when web applications employ server-side templating technologies like Jinja2 or Twig to produce dynamic HTML responses. When user input is integrated with a template in an unsafe manner, SSTI vulnerabilities exist, resulting in remote code execution on the server. 

4. Insecure Serialization

Other vulnerabilities, such as improper deserialization, can be used to execute arbitrary commands in addition to the conventional command injection vulnerabilities. This is because the server-side code deserializes the user-supplied serialized material without verifying it.  

Although this is often called the insecure serialization class of vulnerabilities, if the target program fits specific conditions, such as having proper gadgets in the classpath, it eventually leads to command injection. 

Understanding Command Injection with an Example

In the code below, we use a direct OS command “touch” to make changes to the file, which is not secure. A hacker can exploit this. 

const exec = require('child_process').exec;
const fs = require('fs');

exports.createFile = function(username, filename) {
  return new Promise((resolve, reject) => {
    exec('touch tmp/' + username + '/' + filename, (error, stdout, stderr) => {
        if (error !== null) reject(stderr);
        resolve();
    });
  });
}

The best way to prevent command injection is to use parameterized functions like write() that enforce the separation between the arguments instead of eval, process, etc. Safe APIs are not used in the above code. 

Developers should use safe APIs for reading and writing files here instead of eval(), touch, or any other type of direct OS commands and evaluation. And if there are no safe APIs available, sanitize all inputs and remove characters that may be interpreted, such as ‘;’, etc. 

Here is how you can prevent command injection using JavaScript. 

const exec = require('child_process').exec;
const fs = require('fs');

exports.createFile = function(username, filename) {
  return new Promise((resolve, reject) => {
    exec(fs.closeSync(fs.openSync(`/tmp/jail/tmp/${username}/${filename}`, 'w')), (error, stdout, stderr) => {
        if (error !== null) reject(stderr);
        resolve();
    });
  });
}

We use open sync and close sync instead of a direct OS command, touch. 

Types of Command Injection

Command Injection is of two types. One can be predicted by directly looking at the response, and the other can't. Let's take a broader look at the two command injection types with examples. 

1. Result-Based Command Injection

In result-based command injection, the result shows the command's output directly, which means the user can directly see the outcome of the arbitrary command that he wrote in the response. 

<body>
    <div class="container">
      <div class="header">
        <nav>
          <ul class="nav nav-pills pull-right">
          </ul>
        </nav>
        <h3 class="text-muted">Cloud Storage Information</h3>
      </div>
      <h3>
        Files:

       /* Coming from API */
      <ul>
        <li>bye </li>
        <li>hi </li>
        <li>taxes </li>
        </ul>
      </h3>
     
      <div class="jumbotron">
        <h1>New file</h1>
      <form method="post" action="/createnewfile">
          <label for="filename">File Name</label>
          <input type="text" name="inputFilename" id="inputFilename" class="form-control" placeholder="File Name" required="">
          <button type="submit">Create File</button>
        </form>
      </div>
    </div>
  </body>

This input does not have any validation and can lead to command injection. 

Vue Broken Authentication Guide: Examples and Prevention image

This form has an input and a button that can create a file. As you can see, we already have some files in the folder printed in the image.  

Let's check whether it has command injection or not by entering an OS command a; ls; in the input. This would not show any effect in the response but would also not throw an error. And now we know that it allows a semicolon. 

So let's try X; rm -r *. This command removed all the files and broke its functionality. We can now see the result directly in the response, which shows us that this application is vulnerable to result-based command injection. 

2. Blind Command Injection

In blind command injection, the response does not show the command's output. The user cannot predict whether there is a command injection or not just by seeing the response.  

There are two techniques to find out if the application is vulnerable to blind command injection or not. 

2.1 The Time-Based Technique (Blind)

From the delay in the execution time, one can detect this kind of security risk. The ping command for time delay allows you to select the amount of ICMP packets to send and the time it takes to perform the command: 127.0.0.1 ping -c 10. The program will ping its loopback network adapter for ten seconds due to this instruction. Once validated, we may export the output of the injected command char by char using a series of OS commands like cut, head, etc.  

We can use a time-based technique to check whether there is command injection. In the instance of result-based command injection, you can use this technique to check if there is a command injection or not since we could not see it directly in the response the first time. If you enter test1; ping -i 1 -c 15 127.0.0.1, you will notice a delay in the output execution.  

Vue Broken Authentication Guide: Examples and Prevention image

2.2 File-Based Technique (Semi-Blind)

File-based command injection happens when you can write the command in the file that is accessible to us when we cannot directly see the output in the response. An attacker could use this the same way. The difference will be that this time he will be entering the command in the file that we will have access to instead of an input. 

How to Prevent Command Injection Vulnerabilities

The problem with command injection is straightforward: if your code makes a call to an operating system command in the same manner that a user would type it, you are putting the server at risk. 

The most effective way to prevent OS command injection vulnerabilities is by never running OS commands from the application. 

Suppose your web application must call OS commands. You should use a framework or module such as the fs module in JavaScript that prevents OS command injection attacks. You can do this by automatically escaping or encoding user-submitted data or preventing user-submitted data from being executed as a command. 

There are several advantages to this approach. First, it simplifies and strengthens the application's design and its architecture. In particular, it allows developers to think about the application in terms of higher-level tasks rather than lower-level commands. Second, it reduces the possibility of introducing vulnerabilities such as command injection. 

Best Practices to Avoid Security Vulnerabilities

Thousands of security vulnerabilities are discovered every year in all kinds of applications. We put together a list of best practices to avoid them. 

1. Use Strongly Validated User Inputs

No matter the data source or data type, and regardless of the programming language, input validation is vital. You can implement this by whitelisting strings or characters. You can use regex to allow only specific characters like alphanumeric and restrict others. 

No matter what programming language you use or how big your codebase is, this practice will always protect you. 

2. Use the Principle of Least Privilege 

The principle of least privilege (PoLP) restricts an individual's privileges to the rights required to perform their duties. The objective is to reduce the likelihood and severity of successful assaults.

3. Keep Public and Private Data Separate 

Another important tip for securing private data is to keep the public and private data separate. This can help keep the private data safe even if the attacker gets access to public data. He will get only the public data. 

4. Automate Testing for Command Injection in the Build Pipeline

In the era of cyberattacks and data breaches, organizations increasingly focus their attention on data security. Automation is one of the best ways to tackle the growing volume of vulnerabilities and ensure that data is secure. A dynamic application security testing (DAST) solution can scan for and identify vulnerabilities regularly and automatically. This is where StackHawk is invaluable.  

StackHawk's DAST solution provides unique features like automated preproduction scans, microservices scans, API scans, and many other valuable features to secure your application before it goes to production. 

Find and Fix Security Vulnerabilities

Conclusion 

You can learn even more about command injection vulnerability on our blog. And as a reminder to our readers, we're always here to help you with any security-related problems you might be facing. So if you have any questions about this or any other security-related topic, don’t hesitate to contact us


StackHawk  |  April 25, 2022