Nivel AvanzadoPremium

XS-Leaks — cross-site timing attacks, error-based leaks, frame-counting

Técnicas XS-Leaks 2026: timing attacks con performance.now(), error-based leaks via tag onerror/onload, frame-counting con CSP violations, ID-based leaks via element selector y storage timing.

Gorka El Bochi11 de mayo de 202616 min

Deja de leer sobre bugs y empieza a cazarlos

Sin tarjeta · Academy gratis · cancela cuando quieras

Crear cuenta gratis

Respuesta rápida

XS-Leaks son canales laterales que permiten leer información binaria o pequeña sobre una víctima logueada en otro origen — sin necesidad de XSS, sin sortear SOP directamente. Se basan en diferencias observables desde una página que solo embebe contenido cross-origin: tiempos de carga, errores de parseo, cantidad de frames, eventos disparados. Útiles para detectar si la víctima está logueada, si es admin, si tiene cierto rol, o filtrar IDs uno-bit por uno-bit.


El modelo de threat

El atacante:

  1. Hospeda attacker.com.
  2. La víctima visita atacante.com (phishing, ads, click-through).
  3. Atacante embebe / fetcha target.com cross-origin.
  4. Atacante observa side channels del browser para inferir datos.

Mitigaciones modernas: COOP, COEP, CORP, SameSite=Lax por defecto. Pero muchos endpoints siguen siendo leakables porque la mitigación se aplica a las páginas top-level, no al universo de subrecursos.


Canal 1 — Timing attacks con performance.now()

El más universal. Mide cuánto tarda un recurso cross-origin en cargar. Diferencias de 20-100ms suelen ser observables.

Setup básico

javascript
async function timeFetch(url) {
  const start = performance.now();
  await fetch(url, { mode: 'no-cors', credentials: 'include' });
  return performance.now() - start;
}

const tLogged    = await timeFetch('https://target.com/api/me');
const tSomething = await timeFetch('https://target.com/api/admin-only');

// Si tSomething < tLogged significativamente, admin-only retornó rápido (403)
// Si son similares, retornó datos (admin)

Endpoints clásicos leakables por timing

EndpointDiferencia observable
Search con un término que existe vs uno que noTiempo de SQL query
/admin/dashboard vs /admin/non-existent200 vs 404
Email reset con email existente vs noEmail send delay
Endpoint con caché HIT vs MISSCaché latency
Endpoint con N rows (paginación)Render time

Anti-timing-detection: usa cache eviction

Si el browser cachea las responses, el segundo request es ~0ms. Para evitarlo:

javascript
const url = `https://target.com/api/me?bust=${Math.random()}`;

Y mide múltiples veces para promediar jitter de red.


Canal 2 — Error-based leaks

Algunos recursos cross-origin disparan onerror / onload en <script>, <img>, <link> dependiendo del response. Aunque no puedes leer el contenido, sí puedes detectar si el response fue 200, 404, parseable o no.

Script tag onerror/onload differential

html
<script src="https://target.com/api/private/secret-id-12345"
        onload="known()" onerror="notFound()"></script>
  • Si el endpoint devuelve JS válido (200, content-type js) → onload.
  • Si devuelve 404 → onerror.
  • Si devuelve JSON inválido como JS → onerror.

Permite oracle: "¿existe el ID 12345 en el target para este usuario?"

Image dimensions leak

javascript
const img = new Image();
img.onload = () => console.log(img.naturalWidth, img.naturalHeight);
img.src = 'https://target.com/avatar/admin';

Si avatares de admin tienen tamaño distinto que regulares → leak.

CSS load differential

html
<link rel="stylesheet" href="https://target.com/themes/admin.css"
      onload="isAdmin()" onerror="notAdmin()">

CSS dynamically generated por user state → ahora detectable.


Canal 3 — Frame counting

Hasta hace poco, window.length (número de iframes en una página) era leakable cross-origin si embebías la página. Mitigado en navegadores modernos via COOP, pero todavía funciona contra apps sin COOP.

javascript
const w = window.open('https://target.com/dashboard');
setTimeout(() => {
  console.log(w.length); // Número de iframes en el dashboard
  // Apps que tienen iframes condicionales (e.g., "Admin panel" solo para admins) leakan rol
  w.close();
}, 2000);

Variante con <object> o <iframe> + onload

Si el browser bloquea el load por X-Frame-Options: DENY, dispara onerror; si no, onload:

html
<iframe src="https://target.com/admin" onload="canFrame()" onerror="cannotFrame()"></iframe>

X-Frame-Options puede estar set condicionalmente por el server (algunas apps lo emiten solo para páginas protegidas) → leak directo de auth state.


Canal 4 — CSP violation reports (el premium)

CSP report endpoints son uno de los XS-Leaks más limpios y menos parcheados. Si el atacante hospeda una página con CSP que apunte el report-uri a su propio endpoint, cualquier violación CSP generada al cargar contenido cross-origin se reporta al atacante.

Sigue leyendo el chain completo

La parte que falta incluye el PoC paso a paso, código de explotación y la cadena completa que llevó al impacto. Disponible para suscriptores.

Gratis · sin cuenta

La checklist que repaso en cada objetivo nuevo

47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.

Te das de baja en un clic, desde cualquier correo.

Practica esto en un lab

Xs Leaks Timing

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados

2.786

hunters entrenando

63

labs de hackeos reales

1.767

completaciones

$14.790

pagados por estos bugs

37 flags capturadas esta semana·Hackeos reales de HackerOne · YesWeHack · Bugcrowd·Sin permanencia·Academy gratis

La comunidad

Esto no es una plataforma con usuarios.Es gente rompiendo lo mismo que tú.

Dentro se pregunta, se enseña lo que se ha encontrado y se resuelve en grupo lo que atascaría a cualquiera solo. Se entra gratis: sin cuenta, sin plan y sin dejar el correo.

Entrar a la comunidad de BBLABS en Discord
Entrar al Discord

Es gratis y abierto. No hace falta tener cuenta en BBLABS para entrar.