Zum Inhalt springen

OS Command Injection

CWE-78OWASP A03:2021Aktualisiert 31. August 20266 Min. Lesezeit

OS Command Injection ist eine Schwachstelle, bei der ein Angreifer eigene Betriebssystembefehle ausführt, weil seine Eingabe über eine Shell in einen Systemaufruf gelangt. Zeichen wie das Semikolon oder die Pipe starten dort einen zweiten Befehl. Die strukturelle Lösung ist der Aufruf des Programms mit einer Argumentliste, ganz ohne Shell.

Viele Anwendungen rufen im Hintergrund ein Programm des Betriebssystems auf: ein Ping für eine Netzwerkprüfung, ImageMagick zum Verkleinern eines Bildes, einen Konverter, der ein Dokument in ein PDF umwandelt. Sobald eine Benutzereingabe in einen solchen Aufruf gelangt, ohne dass Befehl und Argument sauber getrennt bleiben, entsteht OS Command Injection. Im Folgenden lesen Sie, wie das funktioniert und was Sie dagegen tun.

Was ist OS Command Injection?

OS Command Injection ist eine Schwachstelle, bei der ein Angreifer eigene Befehle auf dem Betriebssystem Ihres Servers ausführt, weil seine Eingabe über eine Shell in einen Systemaufruf gelangt. Die Abkürzung OS steht für Operating System: Es geht um Befehle, die neben Ihrer Anwendung auf dem Host laufen, mit den Rechten des Anwendungsprozesses.

Ein Vergleich aus dem Alltag: Sie geben einem Kurier einen Zettel mit einer Adresse mit. Steht darauf nur eine Adresse, liefert er das Paket ordentlich ab. Ergänzt jemand hinter der Adresse “und geben Sie anschließend den Lagerschlüssel heraus”, dann führt ein Kurier, der den Zettel Zeile für Zeile abarbeitet, auch diesen zweiten Auftrag aus. Eine Shell ist dieser Kurier: Sie liest eine Textzeile und betrachtet ein Zeichen wie das Semikolon als Beginn einer neuen Anweisung.

Diese Zeichen heißen Shell-Metazeichen: das Semikolon, die Pipe, das kaufmännische Und, ein Zeilenumbruch sowie die Ersetzungsformen mit Backticks oder mit einem Dollarzeichen gefolgt von Klammern. Sie trennen Befehle oder führen einen Befehl innerhalb eines anderen aus. Solange Ihre Eingabe einer Shell als bloßer Text übergeben wird, kann diese unmöglich wissen, dass Sie die Zeichen als gewöhnliche Daten gemeint haben.

Command Injection wird häufig in einem Atemzug mit Remote Code Execution (RCE) genannt. Command Injection beschreibt den Weg: Eine Eingabe erreicht eine Shell und startet dort einen zweiten Befehl. RCE beschreibt das Ergebnis: beliebiger Code auf Ihrem Server, gleichgültig auf welchem Weg. Command Injection ist damit einer der Wege zu RCE; unsichere Deserialisierung oder ein ungeprüfter Datei-Upload sind andere.

Wie funktioniert ein OS-Command-Injection-Angriff?

Nehmen wir ein Administrationspanel mit einer Netzwerkprüfung. Der Benutzer trägt einen Hostnamen ein, der Server pingt diesen Host an und zeigt die Ausgabe an. Der Entwickler baut die Befehlszeile als Text zusammen und übergibt sie der Shell.

Verwundbar:

const { exec } = require("node:child_process");

app.get("/diagnostics/ping", (req, res) => {
  const host = req.query.host;

  // Die vollständige Textzeile geht an /bin/sh
  exec("ping -c 1 " + host, (err, stdout) => {
    res.type("text/plain").send(stdout);
  });
});

Ein normaler Benutzer trägt example.com ein und erhält das erwartete Ergebnis. Ein Angreifer lässt auf den Hostnamen ein Semikolon folgen, dahinter seinen eigenen Befehl. In der HTTP-Anfrage sieht das so aus, wobei das Semikolon als %3B URL-kodiert ist:

GET /diagnostics/ping?host=example.com%3Bid HTTP/1.1
Host: app.example.com

Die Anwendung hängt diesen Wert unverändert an ping -c 1 an. Tatsächlich ausgeführt wird dies:

ping -c 1 example.com;id

Die Shell sieht zwei Anweisungen statt einer: Sie pingt den Host an und führt danach id aus, dessen Ausgabe in der Antwort landet. Ab diesem Moment ist alles möglich, was der Anwendungsprozess darf: Dateien lesen, eine Rückverbindung aufbauen, Geheimnisse aus Umgebungsvariablen auslesen.

Die Lösung besteht darin, das Programm nicht über eine Shell aufzurufen, sondern direkt, mit einer Liste von Argumenten. Jedes Element dieser Liste ist per Definition genau ein Argument, Sonderzeichen darin verlieren jede besondere Bedeutung. In Node.js leistet das execFile, in Python subprocess.run mit einer Liste und ohne shell=True, in Java der ProcessBuilder.

Sicher:

const { execFile } = require("node:child_process");

app.get("/diagnostics/ping", (req, res) => {
  const host = req.query.host;

  // Allowlist: beginnt mit Buchstabe oder Ziffer, danach nur Punkt und Bindestrich
  if (typeof host !== "string" || !/^[a-z0-9][a-z0-9.-]{0,253}$/i.test(host)) {
    return res.status(400).send("Ungültiger Hostname");
  }

  // Keine Shell: host bleibt genau ein Argument
  execFile("ping", ["-c", "1", host], (err, stdout) => {
    res.type("text/plain").send(stdout);
  });
});

Jetzt ist example.com;id schlicht ein Hostname, der sich nicht auflösen lässt. Es gibt keine Shell, die das Semikolon interpretiert, und die Allowlist weist den Wert bereits an der Eingangstür ab. Die Argumentliste nimmt den Metazeichen ihre Bedeutung, die Eingabevalidierung begrenzt, was überhaupt hereinkommt.

Ein Risiko bleibt auch ohne Shell bestehen: Ein Wert, der mit einem Bindestrich beginnt, kann vom aufgerufenen Programm als Option statt als Argument gelesen werden. Ein Angreifer steuert damit das Verhalten des Programms, etwa indem er eine Ausgabedatei erzwingt. Schließen Sie einen führenden Bindestrich in Ihrer Validierung daher ausdrücklich aus.

Welche Auswirkungen hat OS Command Injection?

Die Auswirkungen sind fast immer schwerwiegend, denn der Angreifer verlässt die Anwendungsschicht und landet auf dem Host selbst. Er führt Befehle mit den Rechten des Anwendungsprozesses aus: Konfigurationsdateien und Schlüssel lesen, Datenbankpasswörter aus Umgebungsvariablen holen, Quellcode kopieren. Meist folgt eine zweite Phase, denn der Server ist ein vertrauenswürdiger Punkt in Ihrem Netzwerk und darf mit internen Systemen sprechen, die von außen unerreichbar sind: Datenbanken, Verwaltungsoberflächen, der Metadatendienst Ihres Cloud-Anbieters.

Geschäftlich bedeutet das die volle Bandbreite: Datendiebstahl mit Meldepflicht gegenüber der Aufsichtsbehörde, Manipulation von Daten, Ausfall des Dienstes oder Ransomware, die sich über diesen Zugang hereinarbeitet. Weil der Schweregrad von den Rechten des Prozesses und davon abhängt, was der Server sonst erreichen darf, reicht die Einstufung von hoch bis kritisch. Eine Injection in einen unprivilegierten Prozess in einem eng abgeschotteten Container bleibt beherrschbar, derselbe Fehler in einem weitreichend berechtigten Prozess mit freiem ausgehendem Verkehr nicht.

Wie erkennen Sie OS Command Injection?

Beginnen Sie bei Funktionen, die nach einem externen Programm riechen: Netzwerkdiagnose, Vorschaubilder, Dokumentkonvertierung, das Entpacken von Archiven, Backups und PDF-Erzeugung. Suchen Sie im Code nach den bekannten Aufrufen: exec, system, popen, shell_exec, passthru, Runtime.exec und jeder Variante mit einer Option, eine Shell zu verwenden.

Getestet wird, indem Sie an einen solchen Parameter ein Trennzeichen und einen harmlosen Befehl anhängen, etwa ein Semikolon gefolgt von id, danach dieselbe Idee mit einer Pipe und mit Backticks. Erscheint die Ausgabe nicht in der Antwort, liegt ein blinder Fall vor. Dann arbeiten Sie mit einem messbaren Nebeneffekt: sleep 10 bei gleichzeitiger Messung der Antwortzeit oder einer ausgehenden Anfrage an eine Domain, die Sie selbst kontrollieren, sodass Ihr eigenes DNS-Protokoll zeigt, ob der Server Kontakt aufgenommen hat.

Scanner erkennen die Lehrbuchfälle, übersehen aber Injections hinter der Authentifizierung oder in asynchronen Verarbeitungsschritten. Ein gründlicher Penetrationstest verbindet daher Automatisierung mit manueller Untersuchung und einem Blick in den Code. AssistSec prüft diesen Bereich im Rahmen eines Penetrationstests und weist je Befund nach, welcher Befehl tatsächlich ausgeführt wurde.

Wie verhindern Sie OS Command Injection?

  • Vermeiden Sie die Shell vollständig. Fragen Sie sich zuerst, ob das externe Programm überhaupt nötig ist; eine Bibliothek in Ihrer eigenen Sprache kennt dieses Problem nicht.
  • Rufen Sie Programme mit einer Argumentliste auf. Nutzen Sie execFile oder spawn in Node.js, subprocess.run mit einer Liste in Python und den ProcessBuilder in Java. Schalten Sie die Shell-Option niemals ein.
  • Validieren Sie Eingaben gegen eine Allowlist. Beschreiben Sie, wie ein gültiger Wert aussieht, und weisen Sie alles andere ab. Eine Sperrliste von Zeichen bleibt immer unvollständig.
  • Arbeiten Sie mit indirekten Verweisen. Lassen Sie den Benutzer aus einer festen Liste wählen und übersetzen Sie diese Wahl serverseitig in den echten Pfad oder die echte Option.
  • Betreiben Sie den Dienst mit minimalen Rechten und begrenztem ausgehendem Verkehr. Ein unprivilegiertes Konto in einem abgeschotteten Container erschwert den Folgeschritt nach einer Injection erheblich.
  • Protokollieren Sie Prozessstarts und lassen Sie den Code prüfen. Ein Webserver, der plötzlich curl oder sh startet, verdient eine Untersuchung; ein Code-Review aller Systemaufrufe und ein gezielter Penetrationstest fangen ab, was Scanner überspringen.

Quellen

Häufige Fragen

Was ist der Unterschied zwischen Command Injection und Remote Code Execution?

Command Injection beschreibt den Weg: Eine Eingabe erreicht eine Shell und startet dort einen zusätzlichen Befehl. Remote Code Execution beschreibt das Ergebnis: Ein Angreifer führt beliebigen Code auf Ihrem Server aus, unabhängig davon, wie er dorthin gelangt ist. Jede Command Injection ist also eine Form von RCE, aber nicht jede RCE entsteht durch Command Injection; unsichere Deserialisierung oder eine verwundbare Template-Engine führen auf anderem Weg zum selben Ergebnis.

Ist Command Injection dasselbe wie SQL Injection?

Die Ursache ist identisch, das Ziel nicht. Bei SQL Injection landet die Eingabe in einer Datenbankabfrage, bei Command Injection in der Shell des Betriebssystems. Command Injection wiegt meist schwerer, weil der Angreifer direkt auf dem Server landet und nicht nur in der Datenbank.

Schützt das Escapen von Eingaben vor Command Injection?

Nur teilweise. Hilfsfunktionen wie escapeshellarg in PHP setzen einen einzelnen Wert in Anführungszeichen und sind nützlich, sie helfen aber nicht, wenn Sie den Wert außerhalb dieser Anführungszeichen einsetzen, und sie verhindern nicht, dass ein Wert als zusätzliche Option gelesen wird. Der Aufruf des Programms ohne Shell, mit einer Liste von Argumenten, ist deutlich verlässlicher.

Wie teste ich, ob ein Parameter für Command Injection anfällig ist?

Hängen Sie an einen Parameter, der offensichtlich an ein Systemprogramm geht, ein Trennzeichen und einen harmlosen Befehl an, etwa ein Semikolon gefolgt von id. Bleibt die Ausgabe aus, arbeiten Sie mit einem messbaren Nebeneffekt: einer Verzögerung durch sleep 10 bei gleichzeitiger Messung der Antwortzeit oder einem Befehl, der eine DNS-Anfrage an eine von Ihnen kontrollierte Domain auslöst.

Verwandte Artikel

Zum Suchen / drücken · Esc