OS command injection occurs when an application passes user-controlled input into a system shell as part of a command. Because the shell treats certain characters as separators and operators, an attacker can break out of the intended command and run their own, executing with the privileges of the web process. Impact ranges from reading files to full server takeover. The root cause is invoking a shell and building the command string from untrusted input.
What OS command injection is
Applications sometimes shell out to system utilities, a ping tool, an image converter, a PDF generator, a backup script. When the command is assembled by concatenating user input, for example ping -c 1 + user_host, the shell parses the whole string, including any shell metacharacters the user supplied.
The shell treats characters like ;, |, &, $(), and backticks as command separators or substitutions. So input that contains them stops being data and becomes a second command. The injected command runs as whatever user the web application runs as, which is often enough to read secrets, pivot, or plant a shell.
The abuse and payload
Shown for defensive context:
- Confirm with a separator: in a hostname field, submit
127.0.0.1; idor127.0.0.1 | whoami. Output containing user or uid confirms execution. - Command substitution:
$(id)or `id` embeds the result of one command inside another. - Blind injection: when output is not returned, prove execution by timing,
; sleep 10, or by triggering an out-of-band DNS or HTTP callback the tester controls. - Escalate to a shell: a reverse shell one-liner in the injected command gives interactive access.
Because the payoff is immediate code execution, command injection is among the first things checked against any input that reaches a system utility during application security testing.
How to defend
The strongest fix removes the shell from the equation entirely:
- Do not call a shell. Use language APIs that execute a program directly with an argument array, so arguments are passed as data and never parsed by a shell, for example
execFile(cmd, [arg1, arg2])rather thanexec("cmd " + input). - Avoid shelling out at all when a native library can do the job.
- If a value must reach a command, allowlist it, validate strictly against an expected format rather than trying to block bad characters.
- Run the process with least privilege so a successful injection yields the smallest possible foothold.
Blocklisting metacharacters is fragile; passing arguments as an array is the reliable control.
References
- [1]OWASP: OS Command Injection Defense Cheat Sheet(OWASP)
- [2]PortSwigger: OS command injection(PortSwigger)
- [3]MITRE ATT&CK: Command and Scripting Interpreter (T1059)(MITRE)