Reports
MEDIA[EXPOSICIÓN DE DATOS]#3642600

ssrf_filter reenviaba la cabecera Authorization al seguir redirecciones hacia otro host, filtrando credenciales

La gema Ruby ssrf_filter reconstruía cada petición redirigida con las opciones originales, incluida la cabecera Authorization, aunque la redirección llevase a otro host. Un servidor atacante podía quedarse con el token y reutilizarlo.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

ssrf_filter es una librería de Ruby pensada para hacer peticiones HTTP a URLs proporcionadas por terceros sin caer en SSRF. Cuando la respuesta es una redirección, la librería la sigue, pero lo hace reconstruyendo la nueva petición a partir de las opciones de la petición original: cabeceras, cuerpo y request_proc. No distinguía entre una redirección al mismo host y una a un host distinto.

La consecuencia es que, si una aplicación llamaba a SsrfFilter.get(...) con una cabecera Authorization destinada a un servidor concreto, y ese servidor respondía con un 302 hacia otro dominio, el token viajaba también a ese otro dominio. Quien controle el destino de la redirección recibe la credencial.

El código afectado está en lib/ssrf_filter/ssrf_filter.rb, en el bucle que procesa las redirecciones.

Pasos de reproducción

El investigador montó una prueba local con tres servidores:

  • redirector.test: responde con un 302 hacia collector.test.
  • collector.test: guarda la cabecera Authorization que le llega.
  • api.test: expone /private y solo devuelve datos si recibe el token bearer correcto.
  1. Clonar el repositorio y entrar en él:

``bash git clone https://github.com/arkadiyt/ssrf_filter cd ssrf_filter ``

  1. Ejecutar la prueba de concepto adjunta al reporte:

``bash ruby artifacts/ssrf_filter_redirect_leak_2026-04-01/01_bearer_token_replay.rb ``

  1. El script levanta los tres servidores y llama a SsrfFilter.get(...) contra redirector.test con una cabecera Authorization. La librería sigue el 302 y entrega esa misma cabecera a collector.test.
  1. Con el token capturado en collector.test, el script hace una petición a api.test/private, que responde con los datos protegidos.

Salida esperada:

[:fetch_response, "200", "stolen"]
[:stolen_token, "Bearer service-token-123"]
[:replay_response, "200", "private-data"]

Para poder ejecutarlo todo en una sola máquina, la prueba permite temporalmente las direcciones de loopback. Ese ajuste es solo del laboratorio: el fallo es el reenvío de credenciales al cambiar de host, no el acceso a loopback.

Impacto

Cualquier aplicación que use ssrf_filter para pedir recursos con credenciales (por ejemplo, un token de servicio en Authorization) puede acabar enviándolas a un tercero si la URL inicial redirige a un host que controla el atacante. La prueba demuestra que no se trata solo de que la cabecera quede expuesta: el token robado se pudo reutilizar contra otra API protegida y devolvió datos privados, es decir, robo de credenciales seguido de acceso no autorizado.

El reporte figura como resuelto; no detalla cómo se corrigió ni si hubo recompensa.

Qué aprender de este caso

  • En cualquier cliente HTTP o envoltorio que siga redirecciones a mano, revisa qué opciones se copian de la petición original a la siguiente: si se reutiliza el mismo hash de cabeceras sin comparar host (y esquema y puerto) de origen y destino, hay fuga.
  • Al cambiar de origen en una redirección, elimina como mínimo Authorization, Cookie y Proxy-Authorization, y plantéate no reenviar el cuerpo.
  • Una librería "anti-SSRF" protege contra destinos internos, no contra destinos externos maliciosos: si tu aplicación añade credenciales a peticiones hacia URLs en las que influye un usuario, desactiva el seguimiento de redirecciones o valida cada salto.
  • Al auditar integraciones (webhooks, importadores de URL, previsualizaciones), prueba a que el primer servidor responda con un 302 hacia un colector propio y mira qué cabeceras llegan.