Skip to main content
Category: Attack Techniques

Command Injection

Also known as: OS Command Injection, Shell Injection
Simply put

Command injection is an attack where an attacker supplies malicious input to a vulnerable application in order to execute unauthorized operating system commands on the host server. This typically occurs when an application passes unsanitized user-controlled data to a system shell or command interpreter. Successful exploitation can allow an attacker to run arbitrary commands with the privileges of the application process.

Formal definition

Command injection is a class of injection attack in which an adversary manipulates input accepted by a vulnerable application to cause the host operating system to execute arbitrary commands, typically via a system shell or command interpreter. The vulnerability arises when user-supplied data is incorporated into OS-level calls (such as calls to shell(), exec(), or equivalent system APIs) without adequate input validation, sanitization, or parameterization. Because the injected commands execute within the context of the application process, the impact may include unauthorized file system access, data exfiltration, privilege escalation, or full host compromise, depending on the process's runtime permissions and the host environment.

Why it matters

Command injection represents one of the most severe categories of web application vulnerability because successful exploitation grants an attacker direct access to the underlying operating system, not merely the application layer. Unlike vulnerabilities that expose application data in isolation, command injection can allow an attacker to read and write arbitrary files, exfiltrate sensitive data, establish persistent backdoors, move laterally within a network, or achieve full host compromise, all within the privilege context of the running application process. The breadth of potential impact makes it a high-priority concern for any organization operating internet-facing applications that invoke system-level functionality.

Who it's relevant to

Web Application Developers
Developers building features that invoke OS commands or system binaries, such as file processing, network diagnostics, or image manipulation utilities, are directly responsible for preventing command injection at the code level. The primary mitigation is avoiding shell invocation with user-controlled input altogether, typically by using language-native APIs or parameterized system calls that do not pass data through a shell interpreter. Where OS calls are necessary, developers should apply strict allowlist-based input validation before any user data touches a system call boundary.
Security Engineers and Penetration Testers
Security engineers assessing application attack surface need to identify all locations where user-controlled input flows into OS-level calls, which may not be apparent from external interfaces alone. Static analysis tools can flag potentially dangerous function calls at the code level, but typically cannot determine at analysis time whether runtime input is adequately controlled, making manual review and dynamic testing important complements. Penetration testers probe for command injection by supplying shell metacharacter sequences in input fields and observing application responses, including out-of-band techniques such as time delays or DNS callbacks for cases where output is not reflected directly.
DevSecOps and Platform Teams
Teams responsible for build pipelines, runtime environments, and deployment configurations can reduce the impact of command injection by applying the principle of least privilege to application processes, ensuring that even a successful injection yields limited system access. Container isolation, restricted system call profiles, and runtime application self-protection controls can limit an attacker's ability to escalate or move laterally following an initial injection. These controls do not prevent the vulnerability itself but meaningfully constrain its exploitability and blast radius.
Security Operations and Incident Responders
Command injection attacks that succeed may leave forensic indicators in web server logs, system call audit trails, and network telemetry, particularly when attackers execute reconnaissance commands or attempt to establish outbound connections. Security operations teams should monitor for anomalous process spawning from web server or application processes, unexpected outbound network activity, and access to sensitive file paths that fall outside normal application behavior. These behavioral signals are often more reliable detection mechanisms than signature-based approaches alone, since attackers may obfuscate injection payloads to evade pattern matching.

Inside Command Injection

Unsanitized User Input
Data supplied by a user or external source that is incorporated into a system command without validation, escaping, or neutralization, forming the root cause of command injection vulnerabilities.
Shell Metacharacters
Special characters such as semicolons, ampersands, pipes, backticks, and dollar signs that shells interpret as control operators, enabling attackers to append or chain additional commands to the intended one.
Command Execution Context
The runtime environment in which the injected command executes, including the operating system, shell interpreter, and privilege level of the running process, which determines the potential impact of a successful injection.
Injection Sink
The specific function or API call where user-controlled data is passed to the operating system, such as exec(), system(), popen(), or equivalent calls in various languages and frameworks.
Parameterization and Argument Separation
The practice of passing commands and their arguments as distinct elements rather than as a single concatenated string, preventing the shell from interpreting user-supplied data as control syntax.
Allowlist Validation
A control that restricts accepted input to a predefined set of known-safe values or patterns, reducing the attack surface by rejecting inputs that do not conform to expected formats before they reach an execution sink.
Privilege Level of the Executing Process
The operating system permissions under which the vulnerable application runs, which determines the scope of damage an attacker can cause if command injection is achieved.

Common questions

Answers to the questions practitioners most commonly ask about Command Injection.

Does input validation alone prevent command injection?
Input validation reduces risk but does not fully prevent command injection on its own. Allowlist-based validation can block many malicious inputs, but it is typically insufficient as a sole control because edge cases, encoding variations, and context-specific bypasses may still allow exploitation. The primary defense should be avoiding shell invocation with user-supplied data entirely, or using parameterized APIs that separate commands from arguments. Input validation should be layered on top of these structural defenses, not substituted for them.
Is command injection only a risk when user input is passed directly to a shell?
No. Command injection can occur whenever attacker-controlled data reaches a function that invokes operating system commands or interprets shell metacharacters, regardless of whether the data came directly from a user-facing input field. Indirect sources such as environment variables, configuration files, database values, HTTP headers, and data retrieved from third-party services can all serve as injection vectors if that data is incorporated into shell commands without proper controls.
How should developers identify command injection vulnerabilities during code review?
During code review, look for calls to functions that invoke shell commands, such as system(), exec(), popen(), subprocess with shell=True, or language-equivalent APIs. Trace every argument passed to these functions back to its source to determine whether any portion of that argument can be influenced by external input. Pay particular attention to string concatenation or interpolation used to construct command strings. Static analysis tools can assist in identifying these call sites but may produce false positives and can miss cases where the data flow is complex or passes through multiple functions.
What is the safest approach when an application genuinely needs to execute operating system commands?
When shell command execution is unavoidable, prefer APIs that accept a command and its arguments as separate elements rather than a single shell-interpreted string. For example, using an array-based form of process execution in most languages bypasses shell interpretation entirely. Validate all inputs against a strict allowlist before they are used. Apply the principle of least privilege to the process so that it runs with only the permissions necessary for the intended operation. Log and monitor all command execution activity for anomalous patterns.
How can security testing tools detect command injection, and what are their limitations?
Dynamic application security testing tools can detect command injection by sending crafted payloads containing shell metacharacters and observing application responses, timing differences, or out-of-band interactions. Static analysis tools can identify potentially dangerous function calls and data flows at the code level. However, both approaches have limitations. Static tools may produce false positives when flagging safe usage and false negatives when data flow is obscured. Dynamic tools require the application to be running and may miss injection points that are not reachable through standard test cases or that require specific application state. Neither approach guarantees complete coverage.
How should command injection findings be prioritized and remediated in an existing application?
Prioritize findings based on the accessibility of the injection point, the privilege level of the process executing the command, and the sensitivity of the system. Injection points reachable without authentication and running as a privileged user represent critical risk and should be remediated first. Remediation should focus on replacing shell-invoking code with safer alternatives where possible. Where shell execution must remain, enforce strict allowlist validation on all inputs, restrict the execution environment using OS-level controls such as sandboxing or containerization, and ensure the running process operates under a minimally privileged account. After remediation, retest to confirm the fix is effective and has not introduced new issues.

Common misconceptions

Escaping or encoding user input is sufficient to prevent command injection.
Escaping is fragile and context-dependent. Different shells, operating systems, and encoding schemes handle escape sequences differently, and incomplete or incorrect escaping can still leave applications vulnerable. Parameterization and avoiding shell invocation entirely are more reliable controls.
Command injection only affects applications that explicitly call shell functions like system() or exec().
Command injection can also occur through indirect paths, including library functions, framework utilities, or third-party components that internally invoke shell commands, even when the application code does not appear to make a direct shell call.
Static analysis tools can reliably detect all command injection vulnerabilities in a codebase.
Static analysis can identify known dangerous sink patterns and obvious unsanitized data flows at the code level, but it typically produces false positives where context makes usage safe and false negatives where data flow is indirect, dynamically constructed, or passes through external libraries. Runtime context, such as actual input values and execution paths, is generally required to confirm exploitability.

Best practices

Avoid invoking shell interpreters altogether when possible. Use language-native APIs or library functions that interact directly with the operating system without spawning a shell, eliminating the risk of shell metacharacter interpretation.
When OS command execution is necessary, pass the command and each argument as separate elements in an array or structured parameter list rather than constructing a single concatenated string, preventing user input from being interpreted as shell syntax.
Apply strict allowlist validation on any input that will be used in or near a command execution context, accepting only values that match a known-safe set or pattern and rejecting everything else before it reaches an execution sink.
Run application processes under the least privilege required for their function, so that if command injection is achieved, the attacker's ability to access files, modify system state, or move laterally is limited by the process's restricted permissions.
Incorporate both static analysis and dynamic testing into the development pipeline to identify dangerous sink usage and data flow patterns early, while recognizing that static tools may miss indirect or dynamically constructed injection paths that require runtime verification.
Conduct regular code reviews with explicit attention to all locations where external input intersects with OS command execution, including third-party and library code, to identify injection sinks that automated tools may not surface.