Taint-Analyse Schutz vor faulen Daten

Angriffspunkte der fehlerhaften Daten erkennen mit Taint-Analyse
Angriffspunkte der fehlerhaften Daten erkennen mit Taint-Analyse

»Taint« steht für einen Makel. Im Kontext von Software heißt das: Systeme mit ungültigen oder unerwarteten Daten füttern, um sie dann zu manipulieren. Diese Angriffe sind gar nicht so einfach zu erkennen.

Im Sprachgebrauch des sicheren Programmierens werden ungeprüfte Eingabewerte als tainted benannt. Solche Eingabewerte sollten für Entwickler immer ein Grund zur Sorge sein, weil ungeprüfte Datenwerte Qualitätsprobleme wie unerwartetes Verhalten und Systemabstürze verursachen können. Dieselben Techniken, die vor Sicherheitslücken schützen, dienen auch zum Schutz vor unseriösen Datenwerten. Dadurch unterstützen die Techniken der Taint-Analyse effizient bei der Suche und Qualitätsverbesserung des Codes der besonders risikobehafteten Codepassagen.

Besonders gefährdet sind Systeme, die Code von verschiedenen Anbietern enthalten oder Open-Source Code oder Code von vertrauenswürdigen Dritten. Denn laut Forschungserkenntnissen treten Sicherheitslücken vermehrt an den Grenzen zwischen Code-Bestandteilen auf – oft als Folge von harmlosen Uneinigkeiten bei der Interpretation der Schnittstellenspezifikationen. Schützen können sich Programmierer dagegen, indem sie Input aus potenziell riskanten Kanälen bis zu deren Überprüfung als gefährlich einstufen. Die Kontrolle, dass ein Programm ungeprüfte Daten richtig verarbeitet, kann schwierig sein, denn das bedeutet, deren Fluss durch die Code-Struktur nachzuverfolgen. Eine mitunter mühsame Aufgabe, sogar bei relativ kleinen Programmen, die für die meisten tagtäglichen Anwendungen manuell nicht ausführbar ist.

Ungeprüfte Daten einspeisen, beliebigen Code ausführen

Die größten Risiken, die Werte aus einem ungeprüften Kanal anhaften, bestehen darin, dass Angreifer durch sie Sicherheitslücken ausnutzen können oder dass das Programm abstürzt. Tainted Data kann die Ursache vieler Probleme sein, u.a. Speicherüberläufe, SQL Injections, Command Injection, Cross-Site Scripting, arithmetische Überläufe und Path Traversal (Details unter: Common Weakness Enumeration, http://cwe.mitre.org). Viele der folgenschwersten Cyber-Angriffe der letzten zwei Dekaden wurden von den berüchtigten Buffer Overruns hervorgerufen. Weil diese Schwachstelle so weit verbreitet ist und weil sie die Wichtigkeit der Taint-Analyse so gut aufzeigt, soll sie hier detaillierter betrachtet werden.

Es gibt mehrere Möglichkeiten, wie ein Angreifer einen Buffer Overrun ausnutzen kann. Der klassische Fall ist, dass der Angreifer den Ablauf so beeinflusst, dass er beliebigen Code ausführt. In diesem Beispiel ist der Buffer auf dem Stack. Betrachten Sie diesen Code:

void space config left parenthesis void right parenthesis
left parenthesis
space space char space buf left square bracket 100 right square bracket :
space space int space count :
space space...
space strcpy left parenthesis buf comma
getenv left parenthesis " CONFIG " right parenthesis right parenthesis :
space...
right parenthesis

X

Die Eingabe kommt von draußen über einen Aufruf an getenv, das den Wert der Umgebungsvariablen namens CONFIG abruft. Der Programmierer dieses Codes erwartete, dass der Wert der Umgebungsvariablen in den Buffer passt, aber das wird nirgendwo sichergestellt. Erlangt der Angreifer die Kontrolle über den Wert dieser Umgebungsvariablen, dann löst die Zuteilung eines Wertes mit einer Länge von über 100 einen Buffer Overrun aus. Bei buf handelt es sich um eine automatische Variable, die auf dem Stack als Teil des Aktivierungssatzes für den Ablauf hinterlegt ist. Darum wird jedes Zeichen nach den ersten 100 auf die Teile des Programm-Stack außerhalb der Grenze von buf geschrieben. Die Variable namens count darf überschrieben werden – abhängig davon, wie der Compiler den Platz auf dem Stack zuteilt. Wenn dem so ist, dann kontrolliert der Hacker den Wert dieser Variablen. Als echten Gewinn für den Angreifer enthält der Stack die Adresse, zu der das Programm springt, sobald es den Vorgang abgeschlossen hat. Um diese Schwachstelle auszuschöpfen, kann der Angreifer den Wert der Variablen auf einen speziell gestalteten String mit einer Return-Adresse seiner Wahl setzen. Sobald die CPU das Ende der Funktion erreicht, wird sie zu dieser Adresse zurückkehren anstelle zur Adresse im Funktionsaufruf.

Wird der Code in einer Umgebung ausgeführt, in der der Angreifer keine Kontrolle über den Wert der Umgebungsvariablen hat, kann er diese Schwachstelle möglicherweise nicht ausnutzen. Dennoch ist der Code ganz offensichtlich sehr gefährlich. Ein Programmierer könnte ihn in einem anderen Programm wiederverwenden, wo sein Einsatz nicht sicher wäre. In diesem Beispiel kommt der Input aus der Umgebung, aber der Code wäre genauso gefährlich, würde der String von einer anderen Input-Quelle gelesen, beispielsweise dem Dateisystem oder einem Netzwerkkanal.