
El filtro SSRF verificaba el texto del nombre de host, pero el destino real se decidía después mediante DNS. Ese desfase permitió que las URL de Webhook controladas por el atacante alcanzaran objetivos de loopback, metadatos y redes privadas.
El filtro SSRF verificaba el texto del nombre de host, pero el destino real se decidía después mediante DNS. Esa brecha permitió que URLs de Webhook controladas por el atacante alcanzaran loopback, metadatos y objetivos de red privada.
Encontré este problema mientras revisaba Typebot, un creador de chatbots de código abierto, con una simple pregunta de seguridad en mente:
¿Qué sucede si la protección SSRF valida el texto del nombre de host, pero el destino real se decide después mediante DNS?
En este caso, esa pregunta llevó a un error real.
La protección SSRF de Typebot para los bloques Webhook / Petición HTTP validaba solo:
No resolvía los nombres de host antes de permitir la petición.
Eso significaba que un nombre de host como ssrf-repro.example podía parecer inofensivo durante la validación, y luego resolverse a:
127.0.0.1169.254.169.254y aun así ser obtenido por el cliente HTTP del backend.
Ese problema se convirtió en .
Typebot: Typebot en GitHub
CVE: CVE-2026-34207
Corregido en: 3.16.0
Esto afectó a Typebot, una plataforma de chatbot de código abierto ampliamente utilizada. En su sitio oficial, Typebot se presenta como confiable para más de 650 empresas en todo el mundo. El sitio también publicita más de 2 millones de chats mensuales y más de 1,5 millones de bots publicados.

URL de Webhook controlada por atacante -> nombre de host pasa validación SSRF solo literal -> sin resolución DNS antes de la decisión de permitir -> cliente HTTP del backend resuelve nombre de host a destino interno -> petición del lado del servidor alcanza loopback / metadatos / red privada -> los datos de respuesta están disponibles a través de registros de ejecución
Typebot es un creador de chatbots.
Permite a los usuarios crear flujos que pueden:
Webhook / Petición HTTPEso significa que la ejecución de peticiones salientes es un límite de seguridad real.
La pregunta importante aquí no era si Typebot soportaba bloques Webhook.
La pregunta real era:
¿La protección SSRF valida el destino real al que se conectará el servidor, o solo el texto del nombre de host que aparece en la URL?
En este caso, solo validaba la forma textual primero.
Ese fue el error.
Las defensas SSRF fallan de maneras muy predecibles.
La mayoría de las veces, los errores interesantes no son:
169.254.169.254"localhost"Los errores más significativos son errores de límite:
Ese era el lugar adecuado para buscar aquí.
Typebot ya tenía lógica de endurecimiento SSRF para IPs literales de metadatos, loopback, rangos privados y trucos de IP codificadas.
Eso hizo obvia la siguiente pregunta:
¿Qué sucede si el nombre de host no es literalmente peligroso, pero se resuelve a un destino peligroso más tarde?
Eso es exactamente lo que sucedió.
El problema raíz fue la validación del destino basada en el texto del nombre de host en lugar de la dirección IP resuelta.
En la implementación vulnerable, validateHttpReqUrl():
http: y https:metadata.google.internal, metadata.goog, metadata y localhostEsa es la parte importante.
Si el nombre de host era un valor normal como:
ssrf-repro.example
entonces parseIPAddress(hostname) devolvía null, y el validador se detenía allí.
No ocurría resolución DNS antes de la aprobación.
Por lo tanto, la lógica vulnerable se reducía efectivamente a:
const ip = parseIPAddress(hostname);
if (ip) {
validateIPAddress(ip);
}
Eso significa:
La segunda mitad del error estaba en la ruta de ejecución.
En executeHttpRequest(), Typebot primero ejecutaba la validación y luego realizaba la petición real con ky(request.url, ...).
Por lo tanto, la secuencia era:
Esa es toda la vulnerabilidad.
La distinción importante es dónde se tomó la decisión de confianza.
Muchos errores parecen pequeños si se describen mal.
Si describes este como:
"el filtro de nombre de host estaba incompleto"
suena como un problema de calidad.
Ese no es el problema real.
El problema real era:
Eso no es una debilidad cosmética de filtrado.
Eso es una falla de límite de confianza.
Y debido a que el ejecutor HTTP registraba los datos de respuesta en los registros de ejecución, el problema ni siquiera era ciego en los casos más fuertes.
Así que esto no era solo:
Era:
Eso es una verdadera vulnerabilidad SSRF.
Usé dos capas de prueba porque demostraban dos cosas diferentes.
La primera PoC aisló la causa raíz limpiamente.
Usé un pequeño entorno local que:
127.0.0.1http://ssrf-repro.example:18080/...ssrf-repro.example se resolviera a 127.0.0.1Eso demostró la falla exacta:
La salida capturada mostraba:
Eso probó directamente la brecha de validación.
La segunda PoC mostró el error a través de la ruta de funcionalidad real que importa.
La reproducción más simple era:
Webhook que apunte a ese nombre de host benignoUn bloque representativo se veía así:
{
"id": "blk-webhook",
"type": "Webhook",
"options": {
"webhook": {
"method": "GET",
"url": "http://ssrf-repro.example:8000/"
}
}
}
Con una entrada de archivo hosts como:
127.0.0.1 ssrf-repro.example
el validador aún aceptaba la URL porque:
ssrf-repro.example no estaba en blockedHostnameslocalhostparseIPAddress("ssrf-repro.example") devolvió nullLuego el cliente HTTP del backend resolvió el nombre de host a 127.0.0.1 y se conectó de todos modos.
Eso estableció la afirmación completa:
La primera PoC prueba la causa raíz.
La segunda PoC prueba el impacto en el producto.
Esa división importa.
Si solo muestras:
"este filtro acepta un nombre de host"
no has mostrado suficiente.
Si solo muestras:
"ocurrió una petición interna"
no has aislado el porqué.
El informe más sólido es:
Esa es la historia completa.
Este problema se clasificó razonablemente como Alta.
La clasificación del aviso fue:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L
Eso tiene sentido.
Esto no era un SSRF no autenticado a nivel de Internet por sí mismo.
Los privilegios requeridos eran Bajos porque el error independiente requería un actor que pudiera configurar o activar un bloque Webhook / Petición HTTP.
Pero dentro de ese límite, el impacto era grave:
Eso es un problema SSRF fuerte por sí mismo.
Y se vuelve aún más peligroso cuando se combina con otros errores que amplían la accesibilidad.
Algunas personas ven SSRF autenticado y lo subestiman de inmediato.
Eso es un error.
La pregunta real no es:
"¿El atacante había iniciado sesión?"
La pregunta real es:
"¿Podría ese atacante hacer que el servidor se conectara a algún lugar que el modelo de seguridad debía prohibir?"
Aquí, la respuesta fue sí.
El validador afirmaba proteger:
Pero una brecha de resolución de nombres de host permitió que esos mismos destinos volvieran a entrar a través de una representación diferente.
Ese es exactamente el tipo de error que merece un CVE.
La corrección fue sólida porque corrigió el límite real, no solo un síntoma.
En la implementación parcheada, validateHttpReqUrl() se modificó para resolver nombres de host antes de aprobar la petición.
El validador ahora:
lookup de node:dns/promisesEsa es la corrección correcta porque cambia la decisión de confianza de:
a:
Esa es la propiedad de seguridad que debería haber existido desde el principio.
La remediación también agregó cobertura de regresión para:
Ese es el tipo de endurecimiento que quieres en una corrección SSRF real:
El problema se resolvió en Typebot 3.16.0.
Este problema se informó de forma privada a través del flujo de informes de seguridad de GitHub.
El informe incluía:
validateHttpReqUrl()executeHttpRequest()Los mantenedores aceptaron el problema, corrigieron la lógica de validación y la vulnerabilidad se publicó posteriormente como:
CVE-2026-34207
con la corrección lanzada en Typebot 3.16.0.
La lección clave aquí es simple:
las decisiones de seguridad deben tomarse sobre la misma representación que usará la pila de red.
Eso suena obvio.
Pero muchas protecciones SSRF fallan precisamente porque no siguen esa regla.
Eso es suficiente.
Este error también refuerza algo importante sobre la revisión SSRF en general:
Si no resuelves y validas el destino real, tu filtro SSRF sigue siendo incompleto.
Esa es la verdadera conclusión.
Esta vulnerabilidad no trataba de un payload elegante.
Trataba de hacer la pregunta de límite correcta.
En Typebot, el validador SSRF verificaba primero el texto del nombre de host. El destino real se decidía después mediante DNS. El cliente de red seguía la dirección resuelta.
Esa brecha fue el error.
Por eso esto se convirtió en CVE-2026-34207.
Corregido en Typebot 3.16.0.