Skip to main content

Krampus beschert eine Struts-Sicherheitslücke zum Jahresende

Artikel von
feature snyk honeycomb

2. Januar 2024

0 Min. Lesezeit

Anmerkung der Redaktion: 16. Januar 2024

Ein Codebeispiel in diesem Blog wurde aktualisiert, um die Sicherheit der Lösung zu erhöhen.

Am 20. Dezember 2023 aktualisierte NIST einen CVE-Eintrag, um eine neue Path-Traversal-Sicherheitslücke in struts-core aufzunehmen. Es handelt sich um CVE-2023-50164, der auch in der Snyk-Vulnerability database aufgeführt ist und einen kritischen CVSS-Schweregrad von 9,8 aufweist. Wenn Sie schon lange in der Cybersicherheit tätig sind, erinnern Sie sich an den Equifax-Datendiebstahl von 2017, der ebenfalls auf eine nicht behobene Struts-Sicherheitslücke zurückzuführen war.

In diesem Beitrag erläutere ich das Problem, bespreche seinen Schweregrad, führe Sie durch einen Proof-of-Concept-Exploit und gebe Empfehlungen zur Behebung. Wenn Sie Snyk nutzen (melden Sie sich hier kostenlos an), können Sie sich ganz einfach über diese und andere Sicherheitslücken benachrichtigen lassen und erhalten Tipps, wie Sie fehlerhaften Code beheben.

Beginnen wir mit den Empfehlungen zur Behebung. Aktualisieren Sie einfach Ihre Struts-Version auf 2.5.33 oder 6.3.0.2 (oder höher), je nachdem, welche Basisversion Sie derzeit verwenden.

Wie schwerwiegend ist die Struts-Path-Traversal-Sicherheitslücke CVE-2023-50164?

Wir haben bereits das extremste Beispiel einer Struts-Sicherheitslücke gesehen, die zur Ausführung beliebigen Codes führen kann. Diese Sicherheitslücke führte 2017 zum berüchtigten Equifax-Datendiebstahl. Der Exploit gelang, indem ein fehlerhafter Content-Type-Header übermittelt und die daraus resultierende Ausnahme ausgenutzt wurde. Es war die schlimmste Art von Sicherheitslücke, da ein Exploit aus der Ferne möglich war und es damals keine Code-Schutzmaßnahme dagegen gab.

Diese neue Sicherheitslücke ist ernst zu nehmen – Sie sollten unbedingt Ihre Struts-Version aktualisieren. Sie ist jedoch weniger schwerwiegend als die Sicherheitslücke von 2017, da sowohl unsicherer Code als auch die verwundbare Struts-Version erforderlich sind.

Diese Sicherheitslücke ermöglicht einen Path-Traversal-Angriff beim Datei-Upload. Das heißt, Sie können eine Datei hochladen und durch Angabe eines relativen Pfads aus dem vorgesehenen Upload-Ordner „ausbrechen“. Dies kann zur Remote-Codeausführung führen, da der relative Pfad auf Ordner verweisen kann, die von der Anwendung bereitgestellt werden.

Proof-of-Concept-Exploit für die Struts-Path-Traversal-Sicherheitslücke CVE-2023-50164

Den gesamten Code aus diesem Beitrag finden Sie auf GitHub.

Dies ist ein Java-Projekt, das für Builds Maven verwendet. Das Projekt enthält zwei Profile: eines namens vuln (das Standardprofil) und ein weiteres namens no-vuln. Falls Sie Maven oder Maven-Profile nicht kennen, keine Sorge! Die Ausführung ist ganz einfach, und standardmäßig wird das verwundbare Profil verwendet. Voraussetzung ist lediglich eine Java-Laufzeitumgebung ab Version 17. 

Wechseln Sie in den Ordner des geklonten Projekts und führen Sie Folgendes aus:

./mvnw clean jetty:run

Sie können den Server unter http://localhost:9999/struts-vuln-poc aufrufen. Dort sehen Sie eine sehr einfache Oberfläche zum Hochladen von Dateien. Damit beschäftigen wir uns jedoch nicht weiter. Um bei dieser Demo sicherzugehen, dass nichts verborgen ist, rufen Sie http://localhost:9999/struts-vuln-poc/rogue.jsp auf. Es sollte eine 404-Seite wie diese angezeigt werden:

HTTP-404-Fehlerseite mit dem Hinweis, dass die JSP-Datei /rogue.jsp nicht gefunden wurde. Bereitgestellt von Jetty 9.4.46

Wir möchten diese Sicherheitslücke ausnutzen. Öffnen Sie daher ein Terminal, wechseln Sie in den Projektordner und wählen Sie einen HTTP-Client Ihrer Wahl. In meinem Beispiel verwende ich curl, Sie können aber jeden HTTP-Client verwenden, der Datei-Uploads unterstützt. Führen Sie folgenden Befehl aus:

curl \
http://localhost:9999/struts-vuln-poc/upload.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

Rufen Sie nun http://localhost:9999/struts-vuln-poc/rogue.jsp auf. Jetzt sollte die Meldung Ya been PWNED! erscheinen.

Nachdem wir die Sicherheitslücke ausgenutzt und eine schädliche Datei in einem von unserer App bereitgestellten Ordner abgelegt haben, sehen wir uns an, wie es dazu kam.

Wie bereits erwähnt, müssen für diesen Exploit sowohl die verwundbare Version der Struts-Bibliothek als auch unsicherer Code vorliegen.

Sehen wir uns zunächst den unsicheren Code an. Die Aktion Upload hat drei Eigenschaften:

private File upload;
private String uploadFileName;
private String uploadContentType;

Aufgrund der Funktionsweise von Struts werden diese Eigenschaften mit den Daten der Anfrage befüllt, die von einem HTTP-Client gesendet wird – sei es Ihr Browser oder ein Befehlszeilenprogramm wie curl.

Die Methode execute verarbeitet die Anfrage, nachdem die Eigenschaften festgelegt wurden.

Hier sehen Sie den Kern der Methode execute:

String uploadDirectory = System.getProperty("user.dir") + "/uploads/";
File destFile = new File(uploadDirectory, uploadFileName);
FileUtils.copyFile(upload, destFile);

Erkennen Sie das Problem? Statt uns allein auf unsere Fähigkeit zur Fehlersuche zu verlassen, können wir Snyk nutzen, um das Problem in diesem Code zu finden. Snyk bietet eine Befehlszeilenschnittstelle (CLI), IDE-Integrationen und eine Weboberfläche, sodass Sie das Tool überall dort einsetzen können, wo Sie arbeiten. 

Hier sehen Sie die Ergebnisse des CLI-Befehls snyk code test, mit dem sich die statischen Anwendungssicherheitstests (SAST) von Snyk nutzen lassen:

Terminal, in dem die statische Analyse von Snyk eine Path-Traversal-Schwachstelle mit hohem Schweregrad in Upload.java, Zeile 23, erkennt.

Das Ergebnis zeigt mir die genaue Zeilennummer, in der sich eine Sicherheitslücke befindet, sowie deren Typ: Path Traversal. Außerdem sehe ich den Schweregrad des Problems: High.

Hier sehen Sie die Ergebnisse des CLI-Befehls snyk test, mit dem sich die Softwarekompositionsanalyse (SCA) von Snyk nutzen lässt:

Terminalausgabe von Snyk Test mit einer kritischen Sicherheitslücke zur Remotecodeausführung in Apache Struts und der Empfehlung, von Version 6.3.0.1 auf 6.3.0.2 zu aktualisieren

Das Ergebnis zeigt mir, dass ich eine Abhängigkeit mit einer bekannten Sicherheitslücke habe – nämlich meine Version von struts-core, Version 6.3.0.1. Außerdem wird empfohlen, sie auf 6.3.0.2 zu aktualisieren.

Beide Ergebnisse sind äußerst nützlich und ermöglichen konkrete Maßnahmen. Noch hilfreichere und umfassendere Einblicke erhalten Sie jedoch durch die IDE-Integration.

Ich habe das Projekt in IntelliJ IDEA geladen, das ich für die Java-Entwicklung verwende. Der Snyk-Scan des Projekts liefert folgende Ergebnisse:

Die Snyk-Erweiterung für IntelliJ IDEA erkennt eine Schwachstelle zur Remotecodeausführung in einer Struts-Komponente.

Im linken Bereich sehe ich sowohl die SAST- als auch die SCA-Ergebnisse. Im rechten Bereich werden mir konkrete Empfehlungen zur Behebung angezeigt.

Noch hilfreicher: Wenn ich auf das Code-Problem klicke, wird ein Bereich mit Code-Korrekturen aus drei anderen Open-Source-Projekten angezeigt, die dasselbe Problem aufwiesen wie mein Code:

Die Snyk-Erweiterung für IntelliJ IDEA erkennt eine Path-Traversal-Schwachstelle in Struts.

Sehen wir uns das Beispiel google/j2objc genauer an. Beim Herunterscrollen sehe ich die Codezeilen, die zur Behebung dieser Sicherheitslücke geändert wurden, im diff-Format:

Snyk-Sicherheitsscan mit einer kritischen Apache-Struts-Sicherheitslücke und einem Code-Diff, der eine Prüfung zur Validierung des kanonischen Pfads hinzufügt

Das verschafft mir einen großen Vorsprung bei der Codekorrektur, insbesondere wenn ich mit der betreffenden Sicherheitslücke noch nicht gut vertraut bin.

Behebung einer Path-Traversal-Sicherheitslücke

Mit den Informationen aus den Snyk-Scan-Tools kann ich jetzt sowohl meinen eigenen Code als auch meine Abhängigkeiten korrigieren.

Befassen wir uns zuerst mit dem Code. Führen Sie Folgendes mit curl oder einem anderen HTTP-Client aus:

curl \
http://localhost:9999/struts-vuln-poc/upload-no-vuln.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

In der Ausgabe sehen Sie nun die Fehlermeldung Attempted path traversal attack. Mithilfe der Hinweise aus dem Snyk-Code-Scan habe ich den Code wie folgt aktualisiert:

File uploadDirectory = new File(USER_DIR + "/uploads/");
File destFile = new File(uploadDirectory, uploadFileName);

if (
  !destFile.getCanonicalPath().startsWith(uploadDirectory.getCanonicalPath() + File.separator)
  !upload.getCanonicalPath().startsWith(USER_DIR)
) {
  throw new SecurityException("Attempted path traversal attack");
}

FileUtils.copyFile(upload, destFile);

Die if-Anweisung prüft, ob destFile den kanonischen Pfad für unser festgelegtes uploads-Verzeichnis verwendet. Außerdem wird geprüft, ob sich die hochgeladene Datei in den von der App definierten Ordnern befindet und nicht von einem unbekannten Speicherort stammt. Beim Datei-Upload legt Struts die Datei automatisch in einem temporären Ordner innerhalb des Klassenpfads ab. Schlagen diese Prüfungen fehl, wird eine SecurityException ausgelöst.

Das ist eine deutliche Verbesserung gegenüber dem ursprünglichen Code und schützt vor Path-Traversal-Angriffen.

Hier gibt es eine wichtige Feinheit, die erwähnenswert ist. Beachten Sie den abschließenden Schrägstrich (/), der über den Aufruf von File.separator hinzugefügt wird. Das ist sehr wichtig, denn ohne ihn könnte ein Angreifer mithilfe eines Partial-Path-Exploits möglicherweise weiterhin aus dem Upload-Ordner ausbrechen. Der Grund: getCanonicalPath() gibt immer einen Wert ohne abschließenden Schrägstrich zurück. Jonathan Leitschuh erklärt das sehr anschaulich in seinem DefCon-Vortrag: Jonathan Leitschuh. Eine noch robustere Lösung verwendet getCanonicalFile().toPath(); sie ist im Repo zu diesem Beitrag enthalten. 

Entscheidend ist: Ein Update auf die neueste Struts-Version ist der beste und zuverlässigste Weg, das Path-Traversal-Problem zu beheben. Sie müssen sich nicht auf manuelle Code-Prüfungen verlassen, da relative Pfade automatisch entfernt werden.

Wir können die Sicherheitslage weiter verbessern, indem wir auch die übrigen empfohlenen Änderungen aus dem Snyk-Scan umsetzen. Das heißt, wir aktualisieren struts-core von Version 6.3.0.1 auf 6.3.0.2.

Das zuvor geklonte Projekt-Repo enthält ein Profil mit der aktualisierten Version von struts-core. Beenden Sie die laufende App und löschen Sie die zuvor hochgeladene Datei src/main/webapp/rogue.jsp. Starten Sie die App mit folgendem Befehl erneut:

./mvnw clean jetty:run -P no-vuln

Führen Sie nun den ursprünglichen curl-Befehl von vorhin aus:

curl \
http://localhost:9999/struts-vuln-poc/upload.action \
-F "Upload=@./payload/rogue.jsp" \
-F "uploadFileName=../src/main/webapp/rogue.jsp"

Diesmal erhalten Sie eine Erfolgsmeldung wie: File uploaded successfully to /CVE-2023-50164-POC/uploads/rogue.jsp

Beachten Sie, dass der von uns angegebene relative Pfad ignoriert wurde. Vielleicht fragen Sie sich, warum wir die zuvor angezeigte Fehlermeldung nicht erhalten haben. Das liegt daran, dass die aktualisierte Version von struts-core als Eingabe übergebene Pfade automatisch bereinigt. 

Wenn Sie in der Klasse Upload.java in Zeile 23 einen Breakpoint setzen, sehen Sie bei einer curl-Anfrage Folgendes:

Debugger-Ansicht mit uploadFileName auf „rogue.jsp“ sowie Variablen für Upload-Verzeichnis und Zieldatei.

Der angegebene relative Pfad wurde aus uploadFileName entfernt.

Mit Snyk Sicherheitslücken immer einen Schritt voraus

Ich hoffe, diese Demo zur Path-Traversal-Sicherheitslücke in älteren Versionen von struts-core war für Sie hilfreich. Den gesamten Code aus diesem Beitrag finden Sie auf GitHub.

Die Snyk-Scan-Tools haben das Problem sowohl in der Abhängigkeitsdefinition des Projekts als auch im individuellen Code erkannt.

Mit Snyk können Sie Ihre Produktivität noch weiter steigern, indem Sie Ihr Projekt aktiv überwachen lassen und automatisch benachrichtigt werden, wenn neue Sicherheitslücken entdeckt werden. Snyk kann sogar automatisch einen Pull Request (PR) in Ihrem Namen erstellen, sobald eine neue Sicherheitslücke gefunden wird. Sie müssen ihn nur noch prüfen und zusammenführen!

Statt CVEs zu durchsuchen und manuell zu überprüfen, ob Ihr Code ein Problem aufweist, können Sie sich auf das konzentrieren, was Sie am besten können: Code schreiben! Testen Sie Snyk kostenlos unter https://app.snyk.io

Weitere Ressourcen zum Umgang mit Apache Struts:

Starten Sie mit Capture-the-Flag

Erfahren Sie, wie Sie Capture-the-Flag-Herausforderungen lösen – mit unserem jederzeit verfügbaren virtuellen 101-Workshop.