BBLABS v2BBLABSv2
>Inicio>Labs
>New labs

Últimos 3 labs

Cargando…

Ver todos los labs →
>Creators>Ranking
>Aprender

Aprender bug bounty

AcademyGuías, cheatsheets y diccionarioVulnerabilidadesXSS, SQLi, IDOR, SSRF y másHacker RoadmapTu ruta de bug bounty paso a pasoBlogGuías y noticias de bug bounty
>Empresa>Precios
EN
AccederAcceder
>Inicio>Labs>New labs>Creators>Ranking>Aprender>Empresa>Precios
EN
Iniciar SesiónCrear Cuenta
  1. Inicio
  2. Labs
  3. SSRF en un webhook de media que bypassa su guard DNS por TOCTOU (check-then-use sin pinning)
DifícilVDP1h 30min

SSRF en un webhook de media que bypassa su guard DNS por TOCTOU (check-then-use sin pinning)

Por @gorka

El webhook de media valida el host resuelto en el check, pero el fetch real re-resuelve el mismo hostname sin fijar la IP entre medias: alcanza infraestructura interna.

33 visitas1 completadosActualizado sept 2026
Iniciar sesión para empezar

Aprende a encontrar este bug

Un hackeo real de HackerOne, reproducido para que lo practiques.

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· 1

w4tchw0lf1
@w4tchw0lfHace 9 horas

Objetivos

1
Descubrir el webhook público que descarga `media` server-side y confirmar el fetch real.
2
Confirmar que el guard bloquea IPs internas nombrando la IP resuelta y su rango.
3
Leer el directorio de servicios internos y anotar la dirección del servicio objetivo.
4
Detectar en el changelog que validación y descarga corren como pasos separados.
5
Comparar dos operadores equivalentes para distinguir cuál fija (pinea) la IP resuelta.
6
Armar un servidor DNS propio: primera resolución pública, siguientes hacia el interno, TTL=0.
7
Confirmar en el visor de consultas que hubo dos resoluciones distintas antes de dar por bueno el ataque.
8
Leer el adjunto capturado en la bandeja y extraer la flag del servicio interno.

Información

Plataforma
HackerOne
Dificultad
Difícil
Duración
1h 30min
Bounty
VDP
Completados
1
Creador
gorka@gorka
Actualizado
sept 2026

Descarga el entorno

Reprodúcelo y encuentra el bug tú mismo

Crear cuenta

Herramientas

curlBurp SuiteNavegadorToolkit DNS (dado)

Prerequisitos

  • HTTP y APIs REST
  • Fundamentos de DNS
  • SSRF / TOCTOU básico

Tags

SSRFRace ConditionAPI Abuse

Logro que recibirás

Cuando resuelvas este lab desbloqueas este logro compartible

BBLABS.ESLab Resuelto
DifícilVDP
// achievement_unlocked

SSRF en un webhook de media que bypassa su guard DNS por TOCTOU (check-then-use sin pinning)

SSRFRace ConditionAPI Abuse
sept 2026
gorka
solved_by@gorkaMiembro desde mar 2026
bblabs.es// real hacking practice

Writeups de la comunidad

Contenido del lab

Escenario

Convergia es una plataforma SaaS omnicanal: SMS/MMS, email y chat de distintos operadores externos convergen en una única bandeja de agentes. Los operadores entregan mensajes entrantes por webhook y pueden adjuntar "media" — la foto de una entrega, un justificante, un adjunto de soporte — como URLs remotas que la plataforma descarga server-side para renderizarlas dentro de la conversación del agente.

Eres pentester con una cuenta de agente en Convergia. Tu misión: demostrar que ese descargador de media, pensado para traer imágenes inocuas de operadores externos, puede acabar golpeando infraestructura interna que nunca debería ser alcanzable desde fuera de la red del producto — y traerte de vuelta un secreto que vive ahí dentro.

Qué tienes

  • Cuenta demo: attacker@convergia.local / password123 — rol agente: puedes ver la bandeja, leer conversaciones y sus adjuntos. Es tu punto de partida para el read-back final, no es la vulnerabilidad.
  • Acceso a la bandeja completa de Convergia: páginas de Integraciones, Servicios internos y Changelog, más un historial ya seedeado de conversaciones (algunas con media pública ya adjunta, prueba de que el flujo de descarga funciona en producción sin ser el bug).
  • Un segundo servicio, ya desplegado y expuesto en el mismo origen bajo /toolkit: un toolkit de servidor DNS controlable, con una UI que trae un hostname listo y un formulario para "armar" qué IP responde a cada consulta.

Guía paso a paso

  1. Encuentra el webhook y confirma que descarga server-side. En Integraciones ves los dos canales de operador activos, la ruta pública POST /api/v1/channels/inbound/:provider, el shape exacto del payload ({from, to, body, received_at, media:[...]}) y un botón "Send test message" que dispara un ejemplo por ti. Lánzalo, o construye tu propio POST con un media apuntando a cualquier URL pública: la respuesta es 200 y aparece de inmediato una conversación nueva en tu bandeja con la media ya adjunta. Confirmas que el servidor hace un fetch real, server-side, sobre cualquier URL que le mandes — y sin sesión: el webhook es público.

  2. Apunta directo al interno y localiza el guard. En Servicios internos el directorio lista "Ops Metrics" con su dirección exacta (http://10.31.9.10:8080/ · internal network only), junto a 2-3 backends de acompañamiento. Prueba media apuntando esa misma dirección: el adjunto que se genera no es la respuesta del servicio, es un error que nombra la IP resuelta y su rango (media host 10.31.9.10 resolved to 10.31.9.10 (non-routable range: private (10.0.0.0/8)) — refusing to fetch). Conclusión: hay un guard, y valida la IP a la que resuelve el hostname, no la cadena de la URL — el camino directo está cerrado.

  3. Detecta que check y descarga son dos resoluciones separadas, y arma el rebind. El Changelog (v2.0.5, "Media validation pipeline hardening") lo dice con toda honestidad: "media validation and media download run as separate steps; a re-fetch may occur after validation". Repite el mismo experimento del paso 2 contra el segundo operador ("Hardened v2" en su ficha de Integraciones): el adjunto nunca trae datos internos, aunque el hostname resuelva exactamente lo mismo — ese operador resuelve una única vez y conecta directamente a esa IP (la fija/pinea), así que no hay una segunda resolución que aprovechar. La diferencia entre los dos operadores es la prueba de que fijar la IP tras el check es justo lo que falta en el primero.

    Abre el toolkit DNS del lab en /toolkit — un hostname ya preparado (mms-fetch.rbnd.test) y un formulario arm {host, safe_ip, internal_ip}. Ármalo con la IP pública que trae por defecto (198.51.100.20, pasa el check del guard) como primera respuesta, y la IP de "Ops Metrics" del paso 2 (10.31.9.10) como respuesta a partir de la segunda consulta, con TTL=0.

  4. Dispara el webhook contra el hostname armado — la vuln. Manda el mensaje al primer operador (el que NO fija la IP, sablewire) con media apuntando al hostname que acabas de armar en el puerto 8080. El guard resuelve el hostname para el check → obtiene la IP pública → pasa. El fetch real vuelve a resolver el mismo hostname (nadie fijó el resultado del check anterior) → esta vez el toolkit responde la IP interna → el fetch conecta directamente contra "Ops Metrics" y captura su respuesta completa.

  5. Confirma el punto ciego y lee el adjunto — la flag. Antes de dar el paso 4 por bueno, refresca el visor de consultas del toolkit (/log): verás dos entradas para el mismo hostname, una con la IP pública y la siguiente con la interna — la prueba de que hubo dos resoluciones reales y de que el fetch cogió la segunda. Luego ve a tu bandeja, abre la conversación que acaba de crear el webhook y descarga su adjunto: es el JSON completo de "Ops Metrics", con varios campos de acompañamiento (queue_depth, node_id, internal_signing_key, smtp_relay_password) y el campo service_token, que contiene la flag.

Canales de feedback (no vuelas a ciegas)

  • El webhook responde 200 de inmediato y la conversación aparece en la bandeja sin recargar nada (paso 1).
  • El adjunto de un intento bloqueado nombra la IP resuelta y su rango exacto — nunca un 400 genérico (paso 2).
  • El visor de consultas del toolkit lista cada A-query respondida en orden, con su IP — confirma en vivo si hubo una o dos resoluciones (paso 3-4).
  • El adjunto capturado renderiza el JSON completo del servicio interno, no un resumen — la flag está ahí en claro (paso 5).

Decoys (descártalos por razonamiento acotado)

  • El guard directo (funciona). Cualquier media que resuelva a un rango no enrutable (privado, loopback, link-local — incluida la IP de metadata en la nube — CGNAT) se bloquea con el error que nombra la IP y el rango. Un único intento con la IP del directorio de servicios basta para confirmarlo: el guard no está roto, así que ir directo al interno no es el camino.
  • El operador "Hardened v2" (pinning). Comparte el mismo guard, pero tras validar conecta a la IP exacta que resolvió — no vuelve a preguntar al DNS. Armar el mismo rebind y dispararlo contra este operador nunca trae datos internos: confirma que el fallo no es "cualquier operador se puede rebindear", es específicamente la ausencia de fijar la IP tras el check en el operador estándar.
  • Esquemas fuera de http(s). file://, data:, gopher://, etc. se rechazan con 400 unsupported media scheme antes de tocar ningún DNS. Un intento con file:///etc/passwd lo confirma: no hay atajo por esquema, el único camino es http(s) más la ventana entre dos resoluciones DNS.

La falla del laboratorio

Clase: CWE-918 (Server-Side Request Forgery) con CWE-367 (Time-of-Check Time-of-Use) como causa raíz, más CWE-350 (Reliance on Untrusted Inputs — una decisión de seguridad basada en una resolución DNS que el atacante controla) y CWE-306 (Missing Authentication, la condición habilitante: el webhook de entrada no exige sesión). OWASP A10:2021 (Server-Side Request Forgery) + A04:2021 (Insecure Design).

El guard (assertMediaTargetAllowed) es correcto: resuelve el hostname con dns.lookup(host, {family:4}) y clasifica la IP resultante contra la lista completa de rangos no enrutables (RFC1918, loopback, link-local incl. 169.254.169.254, CGNAT, reservados) antes de dejar pasar nada. El problema no está en esa clasificación — está en lo que ocurre después: el flujo estándar (fetchMedia) llama a fetch(url) directamente sobre la URL original, y ese fetch vuelve a resolver el hostname por su cuenta, en un instante distinto y sin conocer el resultado del check anterior. Nada conecta la IP que el guard validó con la IP a la que realmente se conecta la descarga.

Esa ventana es explotable porque el atacante controla el servidor DNS autoritativo del hostname del webhook (el toolkit del lab, o en el mundo real cualquier hostname bajo su propio dominio) y puede responder lo que quiera en cada consulta, con TTL=0 para evitar cualquier caché. Basta con servir una IP pública inocua a la primera consulta (la que hace el guard) y la IP interna real a partir de la segunda (la que hace el fetch) para que el check y el uso diverjan.

La mitigación existe en el propio código, aplicada al segundo operador (fetchMediaPinned): resolver una sola vez, validar esa dirección, y conectar explícitamente a esa IP (con el header Host original para no romper el vhost), en vez de volver a pasar el hostname a una librería HTTP que lo re-resolverá por su cuenta. Es la diferencia entre "confiar en que el nombre siga significando lo mismo" y "fijar lo que significaba en el momento en que lo comprobé".

Lección: cualquier control de seguridad que decida algo a partir de resolver un nombre (DNS, pero el patrón aplica igual a symlinks, rutas de fichero o tokens de un solo uso) deja de ser válido en el instante en que ese nombre puede volver a resolverse de otra forma. Si el check y el uso son dos operaciones distintas, hay que fijar el resultado del check y usar exactamente ese valor — nunca volver a preguntar por el nombre original.

Laboratorio basado en la reproducción anonimizada (empresa ficticia Convergia) de una técnica real de bug bounty sobre un guard SSRF basado en resolución DNS, bypasseado mediante DNS rebinding (TOCTOU) contra el downloader de media de un webhook de mensajería.

Contacto

Practica, aprende y hackea

Plataforma de práctica de bug bounty con labs basados en hackeos reales. Aprende hacking ético en entornos seguros.

contactar→

Síguenos

YouTube
@0xGorka
X
@gorkaelbochi
LinkedIn
gorka-el-bochi-morillo
Instagram
@_.gorkaaa.b
Email
team@bblabs.es

Accede a todos los labs desde 7,99€/mes

Nuevos labs cada semana. Cancela cuando quieras.

Crear cuenta

BBLabs es la plataforma de laboratorios de bug bounty en español donde aprender bug bounty con vulnerabilidades reales extraídas de hackeos pagados en HackerOne, YesWeHack y Bugcrowd. Aquí practicas hacking web —XSS, SQLi, IDOR, SSRF, CSRF y más— en entornos descargables, capturas la flag, lees el writeup y aplicas la técnica en programas activos de bug bounty.

BBLabs es la alternativa en español a HackTheBox, TryHackMe y PentesterLab para quienes quieren practicar bug bounty con hackeos reales en lugar de CTFs artificiales. Desde 7,99€/mes, sin permanencia.

→ Aprender bug bounty desde cero→ Cómo hacer bug bounty paso a paso→ Reportes de bug bounty reales→ BBLabs para empresas y academiasLabsAcademyVulnerabilidadesHerramientasRanking de hackersLabs de XSSLabs de IDORLabs de SSRFLabs de CSRFHackTheBox alternativaHack4u alternativaTryHackMe alternativaPortSwigger alternativaPentesterLab alternativaBug Bounty Labs comparativaHackerOne para practicarOffSec / OSCP alternativaINE / eWPT alternativaHTB Academy alternativaDVWA alternativaJuice Shop alternativaVulnHub alternativaPentesterAcademy alternativaRoot-Me alternativaHackTheBox vs TryHackMeMejores plataformas bug bounty 2026BlogSpoilers¿Qué es el bug bounty?¿Cuánto se gana en bug bounty?OWASP Top 10 explicadoMejores webs para practicar hacking webCómo ser hacker ético desde ceroTutorial de Burp Suite en españolOSCP en español: guía y preparaciónGoogle Dorks para bug bountyCuánto gana un hacker ético en EspañaHerramientas de bug bounty 2026Mejores certificaciones de ciberseguridad 2026Burp Suite tutorialsqlmap tutorialffuf fuzzing webnuclei tutorialHTTP Request SmugglingWAF bypassPrompt injection (LLM)Google Dorks
Hecho cony código
TérminosPrivacidadCookiesComparativaEN

© 2026 BBLABS v2 — Todos los derechos reservados