OS Command Injection

ID

java.command_injection

Severity

critical

Resource

Injection

Language

Java

Tags

CWE:77, CWE:78, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.1

Description

Improper neutralization of special elements used in a command ('Command Injection').

Command injection vulnerabilities occur when an application passes untrusted input to a system shell command without proper validation or sanitization.

Such vulnerabilities allow attackers to execute arbitrary shell commands with the privileges of the user running the application, which may result in complete system compromise.

Attackers exploiting the vulnerability can then install a reverse shell, download and install malware or ransomware, cryptocurrency miners, run database clients for data exfiltration, etc.

Understanding and mitigating this risk is crucial, as it can facilitate data breaches, unauthorized data manipulation, or any type of attack that could be crafted via system commands.

Rationale

In Java, OS command injection generally happens through the use of Runtime.exec() or ProcessBuilder when user input is directly concatenated into command strings.

Consider this vulnerable Java code:

import java.io.*;

public class CommandInjectionExample {
    public static void main(String[] args) {
        String userInput = "some input"; // Assume this comes from an untrusted source
        executeCommand(userInput);
    }

    public static void executeCommand(String input) {
        try {
            // Never concatenate user input into a shell command
            String osCommand = "ping " + input;
            // Unsafe command execution
            Process proc = Runtime.getRuntime().exec(osCommand);

            // ... read the output from the command

        } catch (IOException e) {
            // ...
        }
    }
}

In this example, userInput is directly appended to the operating system command line, allowing for potential injection of malicious commands if input is not properly sanitized or validated.

Remediation

To safeguard against command injection, it is essential to adopt a series of preventive measures:

  1. Avoid Direct Command Execution with Untrusted Input: Avoid using functions for executing shell commands with untrusted inputs. Where command execution is necessary, parameterize inputs as separate command-line arguments, and do not concatenate untrusted inputs into shell commands.

  2. Input Validation and Whitelisting: Perform rigorous input validation to ensure that the input conforms to expected and safe formats. Whitelisting valid input patterns is preferable over blacklisting potentially harmful inputs.

  3. Escape Shell Metacharacters: If the input must be included in a command, ensure any shell metacharacters are properly blacklisted, escaped or sanitized using a dedicated library.

    Characters in { } ( ) < > & * ‘ | = ? ; [ ] ^ $ – # ~ ! . ” % / \ : + , \` are shell metacharacters for most OSes and shells.

    This is difficult to do well, and attackers have many ways of bypassing them so it is not recommended. You have been warned !

By implementing these practices, you can significantly minimize the potential for command injection vulnerabilities, enhancing the application’s resistance to this type of attack.

In Java there are different libraries for running OS commands and programs from strings. It is recommended to minimize the use of Runtime.exec(String) or for executing shell commands with untrusted inputs. Always use the multi-argument methods, like the ProcessBuilder constructor below, where each value is a separate command argument, so shell characters in the input are not misinterpreted.

Here is an example employing ProcessBuilder with arguments rather than shell-interpreted strings:

import java.io.*;
import java.util.Arrays;

public class SecureCommandInjectionExample {
    public static void main(String[] args) {
        String userInput = "127.0.0.1";  // Safe, validated input
        executeCommand(userInput);
    }

    public static void executeCommand(String input) {
        try {
            // Use ProcessBuilder to construct command with arguments
            ProcessBuilder processBuilder = new ProcessBuilder("ping", input);
            Process process = processBuilder.start();

            // ... read the output from the command

        } catch (IOException e) {
            // ...
        }
    }
}

In this revised example, ProcessBuilder takes input as arguments, removing the need to concatenate and avoiding shell interpretation, thus effectively mitigating command injection risks.

If you use something like bash -c <arg> to pass a concatenated command to the shell, it is possible to execute arbitrary shell commands:
// This is not safe !
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", "ping -c 3 " + input);
Process process = processBuilder.start();
// Never concatenate unsanitized user input as a shell command argument
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", "ping", "-c", "3", input);
Process process = processBuilder.start();

Configuration

The detector has the following configurable parameters:

  • sources, that indicates the source kinds to check.

  • neutralizations, that indicates the neutralization kinds to check.

Unless you need to change the default behavior, you typically do not need to configure this detector.

References