3 Arten von Directory-Traversal-Schwachstellen in C/C++
Kirill Efimov
4. April 2022
0 Min. LesezeitDirectory-Traversal-Schwachstellen (auch als Path-Traversal-Schwachstellen bezeichnet) ermöglichen es Angreifern, auf Verzeichnisse zuzugreifen, für die sie keine Berechtigung haben. In diesem Beitrag erfahren Sie, wie Directory-Traversal-Schwachstellen auf Webservern mit C/C++ funktionieren und wie Sie sie verhindern können.
Für unser Security-Research-Team waren die C- und C++-Ökosysteme lange Zeit nicht im Fokus. Das änderte sich, als FossID zu Snyk kam. FossID war auf C/C++ spezialisiert. Dadurch konnten wir mehrere Projekte starten, um den allgemeinen Informationssicherheitsstatus dieser Programmiersprachen besser zu verstehen. Dabei entdeckten wir zahlreiche Schwachstellen mit hohem Schweregrad und freuen uns, unsere Erkenntnisse nun mit Ihnen zu teilen.
In diesem Beitrag konzentrieren wir uns auf die unsachgemäße Beschränkung eines Pfadnamens auf ein eingeschränktes Verzeichnis (CWE-22). Diese Schwachstelle tritt in vielen Varianten auf und wird laut den 25 gefährlichsten Softwareschwachstellen nach CWE auch 2022 weiterhin weit verbreitet und gefährlich sein. Wir untersuchen drei verschiedene Arten von Directory-Traversal-Schwachstellen und zeigen dazu anfällige Codebeispiele, Fehlerbehebungen und mögliche Auswirkungen.
Beliebiges Lesen von Dateien
Webserver stellen häufig statische Ressourcen wie HTML-, CSS- und JS-Dateien bereit. Diese Ressourcen befinden sich in der Regel in speziellen Ordnern (z. B. /static, /www oder /assets) im Dateisystem. Eine Schwachstelle, die das beliebige Lesen von Dateien ermöglicht, entsteht, wenn die Webanwendung den Pfad zur statischen Datei nicht korrekt bereinigt. Dadurch können Benutzer „../“-Pfadsegmente verwenden, um das vorgesehene Verzeichnis zu verlassen und schließlich beliebige Dateien auf dem Datenträger auszulesen.
Sehen wir uns direkt ein Beispiel für diese Schwachstelle an, die wir kürzlich entdeckt haben – im Crow-Webframework. Crow ist ein C++-Microframework zum Betreiben von Webdiensten.
Für dieses Beispiel erstellen wir eine einfache „Hello World“-Anwendung, die aus einer einzigen Datei main.cpp besteht:
Zum Kompilieren des Beispiels verwenden wir Ubuntu: g++ -pthread -o server main.cpp. Anschließend können Sie den Server ausführen, indem Sie im Terminal ./server aufrufen.
Laut Dokumentation stellt Crow standardmäßig statische Dateien aus dem Ordner /static bereit, der sich am selben Speicherort wie die ausführbare Datei server befindet. Das lässt sich testen, indem Sie den Ordner /static und darin eine Textdatei erstellen: mkdir static && echo “test” > static/test.txt
Öffnen Sie nun ein weiteres Terminalfenster und führen Sie curl aus, um zu prüfen, ob es funktioniert:
curl http://localhost:8000/ gibt Hello world! als Antwort zurück.
curl http://localhost:8000/static/test.txt gibt test zurück – tatsächlich der Inhalt der Datei static/test.txt.
Um die Schwachstelle auszunutzen, können wir curl --path-as-is "http://localhost:8000/static/../../etc/passwd" ausführen. In der Antwort sehen wir den Inhalt der Datei /etc/passwd. Entscheidend ist hier das Flag --path-as-is. Es deaktiviert die Pfadnormalisierung in URLs. Das bedeutet, dass der Server genau die von uns angegebene URL empfängt – einschließlich der „../“-Segmente.
Auswirkungen auf die Sicherheit
Das Offenlegen beliebiger Dateien auf einem Produktionsserver ist in der Regel ein kritisches Problem: SSH-Schlüssel, verschiedene Zugangsdaten und der Quellcode des Servers können Angreifern ermöglichen, den Angriff auszuweiten und den Server oder vertrauliche Nutzerdaten zu übernehmen.
Dasselbe gilt für eingebettete Geräte, auf denen besonders häufig C++-Server zum Einsatz kommen. Diese Directory-Traversal-Schwachstelle tritt oft in WLAN-Routern auf, etwa von NETGEAR, Belkin, TP-Link und anderen Herstellern. Mögliche Folgen sind in diesem Fall der Diebstahl der Zugangsdaten zum Admin-Panel und die vollständige Übernahme des lokalen Netzwerks.
In manchen Fällen kann eine Directory-Traversal-Schwachstelle selbst dann gefährlich sein, wenn der anfällige Server lokal auf Ihrem Rechner läuft. Lokale Webserver werden häufig in hybriden Web- und Desktop-Anwendungen wie Electron verwendet. Weitere Informationen zu solchen Angriffsvektoren finden Sie in unserer Untersuchung zu anfälligen VS-Code-Plugins.
Abhilfe
Crow v0.3+4 hat diese Directory-Traversal-Schwachstelle behoben. Das Problem wurde über die Datei app.h eingeführt: Dort fügt der Code einfach einen Teil der URL an den Pfad des statischen Ordners an und ruft die Methode set_static_file_info auf, um die Datei bereitzustellen:
Die Lösung bestand darin, am Anfang der Methode set_static_file_info den Aufruf utility::sanitize_filename(path) hinzuzufügen. sanitize_filename ersetzt alle Vorkommen von „..“ durch „_“ und behebt so die Schwachstelle. Die Lösung ist nicht perfekt, da der Server dadurch keine Dateien bereitstellen kann, deren Name „..“ enthält. Das kommt jedoch vermutlich selten vor und dürfte keine größeren Probleme verursachen.
Beliebiges Schreiben von Dateien
Schwachstellen, die das beliebige Schreiben von Dateien ermöglichen, treten sehr häufig in Codebasen auf, in denen die Anwendung Datei-Uploads unterstützt. Die Ursache ähnelt dem vorherigen Fall, ist aber etwas weniger offensichtlich. Stellen Sie sich ein einfaches HTML-Formular für Datei-Uploads vor:
Wählen wir eine Textdatei namens „test.txt“ mit dem Inhalt „test“ aus. Der Browser erstellt dann eine HTTP-Anfrage, die etwa so aussieht:
Wenn Sie den Anfrageinhalt genauer untersuchen, sehen Sie, dass er mehr als nur den Dateiinhalt enthält. Dazu gehört ein Feld für den Dateinamen, das „../“-Segmente enthalten kann und vom Server korrekt verarbeitet werden muss.
Als Beispiel verwenden wir eine kürzlich entdeckte Schwachstelle, die das beliebige Schreiben von Dateien ermöglicht, im sehr beliebten Embedded-Webserver-Framework Mongoose.
Das Datei-Upload-Beispiel aus dem Mongoose-Repository ermöglicht es Benutzern, Dateien in Teilen hochzuladen. Das ist praktisch, wenn das Gerät, auf dem der Server läuft, nur über wenig RAM verfügt. Sie können das Beispiel erstellen und ausführen, indem Sie das Repository klonen und im Beispielordner den Befehl make ausführen. Der Server lauscht sofort auf Port 8000 und legt /tmp als Zielordner für hochgeladene Dateien fest (wie in Zeile 13 angegeben der Datei main.c.)
Nun können wir mit curl Dateien hochladen. Der Befehl curl -X POST --data-binary "test" http://localhost:8000/upload?offset=0&name=test.txt erstellt auf dem Server eine Datei /tmp/test.txt mit dem Text „test“.
Um die Schwachstelle auszunutzen, können wir direkt vor dem Abfrageparameter name „../“-Pfadsegmente einfügen: curl -X POST --data-binary "pwned" http://localhost:8000/upload?offset=0&name=../malicious-file. Der Exploit erstellt daraufhin die Datei malicious-file im Stammverzeichnis des Dateisystems.
Auswirkungen auf die Sicherheit
Schwachstellen, die das beliebige Schreiben von Dateien ermöglichen, lassen sich nicht so einfach ausnutzen wie solche, die beliebiges Lesen erlauben. In vielen Fällen können sie jedoch trotzdem zur Remote-Code-Ausführung (RCE) führen. Eine RCE ist oft möglich, weil Angreifer wichtige Systemdateien wie /etc/init.d/ oder sogar die ausführbaren Serverdateien überschreiben können.
Abhilfe
Wie im vorherigen Beispiel hat Mongoose die Schwachstelle behoben, indem „..“ aus dem vom Benutzer angegebenen Dateinamen entfernt wird. Die Korrektur ist in Version 7.6 enthalten.
Generell empfehlen wir, keine von Benutzern angegebenen Dateinamen zu verwenden. Nutzen Sie stattdessen zufällige Zeichenfolgen, Zeitstempel oder MD5-Hashes. Das eignet sich oft für Anwendungen mit Datei-Upload-Funktion und hilft, viele weitere Schwachstellen wie XSS zu vermeiden.
Zip Slip
Die dritte Art von Directory-Traversal-Schwachstelle, die wir heute behandeln, ist Zip Slip. Wie der Name schon andeutet, geht es dabei um Schwachstellen in der Logik zur Archivextraktion. Diese Art ist dem beliebigen Schreiben von Dateien sehr ähnlich, hat aber eine kleinere Angriffsfläche, da die Logik zur Archivextraktion nicht so häufig vorkommt.
Zur Veranschaulichung der Zip-Slip-Schwachstelle verwenden wir die anfällige Version 6.1.4 von JUCE, einem Open-Source- und plattformübergreifenden C++-Anwendungsframework.
Diese einfache Anwendung verwendet die JUCE-API, um archive.zip in den Ordner data zu entpacken.
Um zu sehen, wie es funktioniert, können wir ein Beispielarchiv mit einer Datei test.txt erstellen:
Wenn wir unsere Beispielanwendung kompilieren und ausführen, wird der Ordner data erstellt und die Datei test.txt darin abgelegt. Was aber, wenn wir ein Archiv mit einer Datei erstellen, die sich in der Dateihierarchie eine Ebene höher befindet?
In diesem Fall liegt die Datei bad.txt nach der Ausführung unseres Programms außerhalb des Zielordners data. Wie Sie sehen, sind die Sicherheitsrisiken bei Zip Slip genau dieselben wie beim beliebigen Schreiben von Dateien.
Es gibt weitere Varianten von Zip Slip, etwa das Entpacken von Symlinks. Weitere Informationen zu diesem Thema finden Sie in unserem Forschungspapier.
Abhilfe
Die gezeigte Schwachstelle wurde in Version 6.1.5 des JUCE-Frameworks behoben – zusammen mit einer weiteren Variante: Zip Slip über Symlinks. JUCE behob die Schwachstelle, indem geprüft wird, ob der vollständig aufgelöste Dateipfad innerhalb des Zielverzeichnisses liegt.
Allgemeine Empfehlungen zur Behebung
Bei der Untersuchung dieser Schwachstellenklasse hat das Snyk Security Research-Team zahlreiche Codebeispiele mit verschiedenen Dateisystem- und Pfadoperationen geprüft. Dabei haben wir eine allgemeine Faustregel gefunden, mit der Sie sich vor Directory-Traversal-Schwachstellen schützen können.
Wenn Sie auf vom Benutzer angegebene Dateipfade verzichten können, tun Sie es!
Diese Regel gilt für viele Anwendungsfälle, in denen Dateien hochgeladen oder Archive entpackt werden.
Wenn Sie jedoch vom Benutzer angegebene Dateipfade verwenden müssen, empfehlen wir Ihnen die folgende Logik (implementiert mit std):
Der obige Code erfordert mindestens C++17, lässt sich aber auch mit boost implementieren. Falls weder std noch boost in Ihrer Umgebung verfügbar sind, reicht es ebenfalls aus, „..“ durch leere Zeichenfolgen zu ersetzen und anschließend mehrere aufeinanderfolgende „/“-Zeichen zu einem einzelnen zusammenzufassen.
Wenn Ihre Anwendung plattformübergreifend ist und auch unter Windows läuft, sollten Sie außerdem die Trennzeichen für Verzeichnisse normalisieren. Windows akzeptiert meist sowohl Schrägstriche („/“) als auch umgekehrte Schrägstriche („\“) als Verzeichnistrenner. Das bedeutet, dass die Nutzlast „..\“ mit „../“ gleichbedeutend ist und ebenfalls für einen Directory-Traversal-Angriff verwendet werden kann. Am einfachsten stellen Sie sicher, dass Ihr Code unter Windows sicher ist, indem Sie vor der Verarbeitung von Pfaden „/“ durch „\“ ersetzen.
Fazit und wichtigste Erkenntnisse
Wenn Fachleute für Informationssicherheit an C- und C++-Schwachstellen denken, konzentrieren sie sich häufig auf Low-Level-Probleme wie verschiedene Arten von Speicherbeschädigung. Diese haben zwar große Auswirkungen auf das Ökosystem, dennoch sollten wir auch den Anwendungskontext berücksichtigen und neben Low-Level- auch High-Level-Schwachstellen verhindern. Zu Beginn unserer Untersuchung gingen wir davon aus, dass C/C++-Webentwickler gängigen Web-Sicherheitsproblemen zu wenig Aufmerksamkeit schenken. Tatsächlich konnten wir zahlreiche Directory-Traversal-Schwachstellen aufdecken:
CVE-2022-25299: Beliebiges Schreiben von Dateien in Mongoose – einem Embedded-Webserver.
CVE-2022-25297: Beliebiges Schreiben von Dateien in Drogon – einem HTTP-Anwendungsframework auf Basis von C++14/17.
CVE-2021-23520, CVE-2021-23521: Zip Slip in JUCE – einem plattformübergreifenden Open-Source-C++-Anwendungsframework.
CVE-2021-23514: Path Traversal in Crow – einem C++-Microframework.
CVE-2022-25298: Path Traversal in Webcc – einem schlanken C++-HTTP-Client und -Server.
Diese Liste wächst weiter. Wir werden sie aktualisieren, sobald Mitwirkende ihre Fehlerbehebungen veröffentlichen.
Bleiben Sie sicher!
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.
