Pular para o conteúdo
Categoria: Pentest9 min de leitura

XSS Moderno: DOM, Stored e Reflected com Exemplos Reais em Ambiente de Teste

Por Lucas Andrade ·

Tres sabores de XSS dissecados em sandbox com payloads, fluxo de exploracao e mitigacoes via CSP estrita, Trusted Types e sanitizacao com DOMPurify.

XSS Moderno: DOM, Stored e Reflected com Exemplos Reais em Ambiente de Teste

Em 2025 o relatorio da HackerOne registrou XSS como o segundo bug mais reportado em programas publicos, com mediana de USD 750 por achado e picos de USD 20 mil em alvos enterprise. A familia segue viva porque os navegadores evoluiram, mas os pipelines de frontend ainda concatenam strings em innerHTML sem cerimonia. O time Basilisk sobe um laboratorio com tres aplicacoes intencionalmente vulneraveis, registra requisicoes no Burp Suite e percorre cada vetor com payload, contexto e patch. Antes de avancar, garanta que seu lab esteja isolado, porque disparar payloads contra terceiros sem autorizacao escrita continua sendo crime na maioria das jurisdicoes, inclusive sob a Lei Carolina Dieckmann e o Marco Civil.

As tres familias de XSS num relance

XSS e a injecao e execucao de JavaScript do atacante no contexto de seguranca de uma origem alheia. Tres familias cobrem praticamente todos os casos: Reflected (o payload chega na requisicao e e refletido de imediato), Stored (o payload persiste no backend e atinge cada visitante futuro) e DOM-based (a injecao acontece inteiramente no lado cliente, sem o servidor ver o payload). O impacto e o mesmo nos tres: o atacante herda os privilegios da vitima no navegador, pode roubar tokens de sessao, agir em nome do usuario e, contra um admin, tomar a aplicacao inteira.

Reflected XSS

Reflected XSS aparece quando a entrada do usuario volta na resposta HTTP sem codificacao adequada, geralmente via querystring ou formulario GET. No nosso lab uma busca em /search?q= concatena o termo dentro de um h2, entao o classico <svg/onload=alert(document.domain)> dispara no contexto top-level. O detalhe que separa um report amador de um profissional e provar impacto: roubar o cookie de sessao via fetch para um dominio do atacante so funciona se o cookie nao for HttpOnly. Documente a brecha com captura no Burp, com a requisicao exata e o contexto de resposta, porque um report sem requisicao reproduzivel cai na pilha de duplicados ou informativos.

Stored XSS

Stored XSS e a variante mais perigosa porque fica no banco e atinge cada visitante futuro. No DVWA ajustamos para o nivel medium, colamos no campo de comentario o payload <img src=x onerror=...> que codifica o cookie em base64 e envia a um webhook, e vemos o webhook coletar sessoes de moderadores em segundos. A superficie real inclui renderizadores de Markdown, templates de email transacional, exports CSV abertos no Excel e ate metadados EXIF lidos por um dashboard interno. A unica defesa robusta combina sanitizacao na entrada com escape na saida; um sem o outro e teatro, porque um valor salvo limpo dispara mesmo assim ao cair no contexto errado.

DOM-based XSS

DOM-based XSS acontece inteiramente no navegador, entao o payload nunca toca os logs do servidor. O classico location.hash canalizado para document.write ainda viaja em widgets de chat legados e em apps React que entregam dados do hash a dangerouslySetInnerHTML. Adaptamos o nivel 1 do Google XSS Game dentro do lab e demonstramos a caca a sinks abrindo o DevTools, ativando 'Pause on exceptions' e soltando a extensao DOM Invader do Burp. O fluxo de triagem e sempre o mesmo: localizar a fonte (hash, search, postMessage), rastrear ate o sink (innerHTML, eval, setAttribute), validar com payload minimo, depois escalar. Como o servidor nao ve nada, as WAF do lado servidor sao cegas aqui.

Contexto e tudo: o escape correto

O mesmo payload dispara ou falha conforme o contexto de injecao. O corpo HTML precisa de codificacao de entidades HTML, um valor de atributo adiciona tratamento de aspas, um bloco <script> precisa de escape de string JavaScript, uma URL precisa de codificacao consciente de contexto, e um contexto de estilo tem regras proprias. A raiz de quase todo XSS e um valor que cruza de um contexto para outro sem ser recodificado. Por isso a regra da OWASP e: codifique na saida, casado com o contexto, nunca filtre de forma generica na entrada. Um filtro de lista negra que so remove sinais de menor e maior cai na hora contra vetores de atributo como onmouseover.

Montar o laboratorio

Suba hoje o laboratorio com DVWA, OWASP Juice Shop e uma app Next.js enxuta, tudo em conteineres Docker numa rede isolada sem rota para a internet exceto um webhook controlado para a prova de exfiltracao. Roteie todo o trafego do navegador pelo Burp, ative o log do historico de proxy e monte uma colecao no Repeater por vetor. Reproduza os tres vetores ate cada um render um popup, e anote para cada um a requisicao exata, o contexto de injecao e o trecho de resposta. Esse runbook e depois a espinha de reports de bug bounty limpos com reproducao em uma frase.

Mitigacao moderna: CSP, Trusted Types, DOMPurify

A mitigacao moderna deixou de tratar de filtrar sinais de menor e maior ha anos. Content Security Policy nivel 3 com nonce por requisicao mata a injecao inline mesmo quando o atacante enfia HTML na pagina, desde que voce nao ceda e adicione unsafe-inline como fallback. Trusted Types transforma atribuicoes a innerHTML em type error a menos que passem por uma policy registrada. Combinado com DOMPurify para HTML rico, as medicoes do Google mostram cerca de 90% de reducao de superficie. HttpOnly no cookie de sessao neutraliza o roubo classico de cookie, e SameSite amortece o reenvio do credencial para origens alheias.

Shift-left: pegar XSS no CI

Cableie a deteccao no CI rodando Semgrep com javascript.lang.security.audit.xss em cada PR, complementado por plugins de ESLint que marcam dangerouslySetInnerHTML e atribuicoes cruas a innerHTML. Uma passada de DAST contra a instancia de staging pega o que a analise estatica perde, como parametros refletidos em templates de terceiros. O ponto chave e que um sink novo quebre o build, nao apenas gere um report que ninguem le. E assim que a prevencao de XSS vira parte da Definition of Done em vez de um pentest tardio.

Armadilhas comuns

A primeira armadilha e a lista negra: qualquer filtro que so bloqueie payloads conhecidos cai contra variantes de codificacao e event handlers alternativos. A segunda e tratar a WAF como unica defesa, evadida com mistura de maiusculas, comentarios e dupla codificacao. A terceira e uma CSP com unsafe-inline, que na pratica nao bloqueia nada. A quarta e supor que um framework como React e seguro por padrao enquanto dangerouslySetInnerHTML e a injecao em href seguem abertos. A quinta e a falta de HttpOnly, que eleva cada brecha refletida direto para roubo de sessao.

Checklist

Teste cada ponto de injecao nos cinco contextos (corpo HTML, atributo, script, URL, estilo). Confirme se o cookie de sessao tem HttpOnly e SameSite. Verifique a CSP: sem unsafe-inline, nonce por requisicao, script-src restritivo. Teste se Trusted Types esta forcado. Prove o impacto com uma demonstracao inofensiva mas inequivoca (um popup com document.domain ou um callback sem dado real). Documente reproducao em uma frase, impacto e patch sugerido por achado, e mapeie cada achado aos buckets STRIDE de Tampering e Elevation antes de abrir o ticket.

Vetores avancados alem do popup

Um alert(1) prova a injecao, mas um report maduro mostra a cadeia por tras. Com um XSS estavel voce pode ler tokens CSRF direto do DOM e emitir requisicoes em nome da vitima, derrotando a defesa CSRF usual porque a requisicao nasce do origem legitimo. Pela API Fetch o payload pode consultar endpoints autenticados e exfiltrar respostas, incluindo dados de perfil ou paineis de admin. Um service worker registrado via XSS sobrevive ate ao reload da pagina e intercepta trafego futuro. Especialmente subestimada e a combinacao de XSS e um handler postMessage aberto que aceita dados atraves de fronteiras de frame: um unico check de origem faltante transforma um subdominio inofensivo em uma cabeca de ponte. Por isso nunca avaliamos o impacto pelo popup, mas pela acao realisticamente alcancavel: tomada de conta, exfiltracao de dados ou escalada de privilegio para um admin, cada uma com uma prova demonstrada mas nao daninha.

Reporte e divulgacao responsavel

Um achado tecnicamente correto sem report limpo se dissipa. A estrutura que e triada na HackerOne, Bugcrowd e Intigriti e sempre a mesma: um resumo preciso, a URL e o parametro afetados, reproducao em uma frase com o payload exato, um impacto demonstrado e uma sugestao concreta de patch. A prova de impacto fica deliberadamente inofensiva: um popup com document.domain ou um callback sem dados reais de usuario, nunca uma exfiltracao em massa de sessoes vivas. Capturas e um clipe curto aceleram muito a triagem. Fique estrito ao scope do programa; um acerto fora de scope nao e gloria, e risco juridico. Documente a justificativa da vector-string CVSS para que a severidade nao pareca negociavel. Um report que faz o trabalho do defensor de entender e implantar o fix e pago mais rapido e constroi a reputacao que depois leva a convites privados com bounties mais altos.

FAQ: Um framework moderno torna o XSS impossivel?

Nao. React, Angular e Vue escapam bindings padrao automaticamente, mas deixam escotilhas deliberadas abertas: dangerouslySetInnerHTML, v-html, bypassSecurityTrustHtml e atributos href/src carregando URLs javascript:. Essas escotilhas sao a fonte mais comum de XSS em bases modernas. O framework encolhe a superficie mas nao substitui CSP, Trusted Types e escape consciente de contexto.

FAQ: Uma WAF basta contra XSS?

Nao. Uma WAF e uma camada externa util, mas e evadivel por codificacao, mistura de maiusculas e truques de protocolo, e nao ve o XSS DOM-based porque o payload nunca chega ao servidor. Trate a WAF como alerta precoce e reducao de ruido, nunca como substituto do escape na saida e de uma CSP estrita.

Conclusao

Takeaway pratico: suba o laboratorio, reproduza os tres vetores ate render um popup em cada um, depois acople CSP com nonce, Trusted Types e DOMPurify e repita os mesmos payloads. O que ainda disparar merece um ajuste de policy ate quebrar. Registre cada iteracao num runbook pessoal porque os melhores reports de XSS exigem reproducao em uma frase, impacto demonstrado e patch sugerido. XSS nao e um problema resolvido, e uma disciplina: codifique por contexto, empilhe defesa em profundidade e trate cada nova saida como um sink potencial ate provar o contrario.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly