Einblick in Path Traversal Attacken

Ich bin vor Kurzem bei einem recht bekanntem Unternehmen auf eine Path Traversal Lücke gestoßen. Ich möchte in diesem Beitrag diese Angriffstechnik etwas erläutern, sowie Gegenmaßnahmen vorstellen.

Was ist eine Path Traversal Attacke?

Unter einer Path Traversal Attacke versteht man den Typus von Angriffen, bei welchen ein Angreifer einen Pfad zu einer zu schreibenden bzw. zu lesenden Datei manipulieren kann. Ein Angreifer könnte unter Umständen Dateien mit eigenen Inhalten überschreiben, oder sich die Inhalte bestimmter Dateien ausgeben lassen.

Ich denke, man muss hier nicht weiter erläutern, warum man diese Angriffsart als (sehr) kritisch einstuft.

Wie funktioniert eine Path Traversal Attacke?

Stellen wir uns mal folgendes Stück PHP Code vor:

<?php
$file = $_GET['file'];
$content = file_get_contents('files/'.$file);
echo $content;
?>

Das Script nimmt einen String über den file-Getparameter und liest den Inhalt der entsprechenden Datei unter files/ aus. Der Inhalt wird anschließend ausgegeben.

Dieses Stückchen Code kann man relativ einfach missbrauchen, wenn man sich zweier Symbole bewusst ist:

  • ./ => jetziges Verzeichnis, in dem man sich befindet.
  • ../ => übergeordnetes Verzeichnis

Nehmen wir an, dass der folgende Aufruf eine Textdatei ausgeben würde:

index.php?file=tutorial.txt
####Tutorial 01#####
Hier könnt ihr euch ein Tutorial vorstellen.

Der interne Pfad wird zu files/tutorial.txt ergänzt.

Möchten wir das missbrauchen, und uns z.B. den Inhalt der index.php ausgeben lassen, dann rufen wir das Script folgendermaßen auf:

index.php?file=../index.php
<?php
[...]
?>

Der interne Pfad sieht so aus: files/../index.php, was genau der index.php entspricht. Wir erhalten somit die Inhalte dieser, und können den Angriff noch ein wenig weiter bis zum root Verzeichnis treiben.

index.php?file=../etc/passwd
index.php?file=../../etc/passwd
index.php?file=../../../etc/passwd
index.php?file=../../../../etc/passwd
index.php?file=../../../../../etc/passwd

Wir hangeln uns immer weiter zurück in der Baumstruktur bis wir im Rootverzeichnis landen, von welchem wir die /etc/passwd auslesen können. Ab dieser Stelle können wir anfangen alle Dateien,  deren Pfad wir kennen, auszulesen. Häufig gibt es Standardpfade. welche einem die Arbeit erleichtern. Unter anderem:

/etc/passwd
/etc/group
/proc/cpuinfo
/proc/meminfo
/proc/partitions

Mit etwas Glück findet man Konfigurationsdateien, die es einem ermöglichen den Server zu übernehmen, in dem dort entsprechende Logindaten hinterlegt sind.

Wie schütze ich mich vor Path Traversal?

Man kann verschiedene Ansätze verfolgen, um sich vor dieser Art des Angriffs zu schützen. Nicht alle Maßnahmen schützen wirklich, und können umgangen werden.

#1 kritische Zeichen ersetzen

Man könnte auf den Gedanken kommen, die Vorkommnisse von ./ bzw. ../ im übergebenen Parameter durch „“ zu ersetzen.

<?php
$count=0; 
$returnValue = preg_replace('~./|../~', '', '../../../../../etc/passwd', -1, $count);
//$returnValue == etc/passwd
?>

Wie man erkennen kann, haben wir die gefährlichen Zeichen aus dem Eingabestring entfernt. Diese Lösung ist aber nicht perfekt, denn was passiert bei den folgenden Eingabe?

....//....//....//....//....//etc/passwd

Richtig, wir konnten diesen Schutzmechanismus umgehen, in dem wir die Zeichen entsprechend wiederholt haben, sodass unser Angriff wieder durchgekommen ist:

../../../../../etc/passwd

#2 kritische Zeichen ersetzen

Den in #1 genutzten Schutz können wir ausbauen, in dem wir diesen mehrmals hintereinander einsetzen, bis keine Änderung am String mehr erkennbar ist.

<?php
$count=0;
while(true) {
    $newstring = preg_replace('~./|../~', '', $file, -1, $count);
    if($file==$newstring) {
        break;
    } else {
        $file = $newstring;
    }
}
?>

In diesem Codeschnipsel wird der String so lange durch das preg_replace geschoben, bis es keine Änderung an dem String mehr gibt. Bis dahin sollten dann alle Vorkommnisse der kritischen Zeichen ersetzt worden sein. Diesen Schutz könnte man so eigentlich schon nutzen.

#3 Nur bestimmte Zeichen zulassen

Der Ansatz einer Whitelist gefällt mir jedoch noch mehr. Wenn wir wissen, wie unsere Dateien benannt sind, dann können wir entsprechend einen regulären Ausdruck formulieren, der diesen Bereich abdeckt, und alle anderen Zeichen als „invalid“ markiert.

<?php
$matches = null;
$returnValue = preg_match('~^[a-z0-9]+.[a-z0-9]{3,4}$~', $file , $matches);
?>

Mit diesem RegEx lassen wir alle Dateien zu, die

  • mit a-z bzw. 0-9  anfangen
  • Dateiendung mit einem „.“ angedeutet wird
  • mit einer 3 oder 4 Zeichen (a-z bzw. 0-9) langen Dateiendung enden

Beispielsweise:

  • test.txt
  • tutorial.mp3
  • Video.jpeg
  • example01.rar

Alle anderen Muster bzw. Zeichen werden diesen Filter nicht passieren, sodass wir sicher sein können.

Fazit

Ich hoffe ich konnte dieses Sicherheitsthema ein wenig beleuchten und mögliche Angriffsszenarien bzw. Abwehrtechniken vorstellen.

~ Sebastian