
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 CVE-2026-34207.
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í: