flawopen.com/Incidents/capital-one-ssrf-imds-july-2019
SSRF en Capital One: robo de credenciales AWS IMDSv1
La denuncia federal alegó que un WAF mal configurado permitió SSRF hasta IMDSv1 de AWS y expuso credenciales temporales del rol y datos S3 de unos 106 millones. Exige tokens y limita los roles.[10], [2]
La denuncia penal del DOJ describe una cadena como esta: Imagina una recepcionista que recoge documentos en las direcciones que le indicas. Un sistema de reenvío puede permitir que alguien la dirija a una oficina interna. Allí puede obtener una credencial temporal de acceso para la carga de trabajo; esa credencial solo abre las salas permitidas por el rol asociado. El error fue suponer que solo el personal llegaría a esa oficina, aunque se podía dirigir allí a la recepcionista.[10], [8]
Conceptos Clave y Términos
SSRF- Server-side request forgery (
CWE-918): hacer que un servidor envíe una petición elegida por el atacante, de modo que lleve la posición de red y la confianza del servidor.[9] Web application firewall- Un filtro delante de una aplicación web. La denuncia penal alegó que el WAF mal configurado de Capital One podía reenviar peticiones a direcciones internas.[10]
IMDSv1- Cuando IMDSv1 está habilitado, las peticiones de metadatos no exigen un token. La ruta de credenciales de seguridad del rol IAM devuelve credenciales temporales asociadas a ese rol.[7], [8]
IMDSv2- IMDSv2 usa un token de sesión que se obtiene con una petición PUT. Configura
HttpTokens=requiredpara rechazar las peticiones IMDSv1 sin token; esto bloquea muchos reenvíos SSRF que solo pueden enviar un GET simple, pero no elimina por completo el riesgo de SSRF.[7], [6] Credenciales de rol IAM- Claves temporales de AWS emitidas a una instancia. Todo lo que el rol puede hacer lo puede hacer también quien tenga las claves hasta que caduquen.[8]
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-918
- D3-OTF Filtrado del tráfico saliente — La restricción de red saliente de esta guía limita los destinos a los que puede acceder el servicio o el host.
Cronología del Incidente
Un investigador externo avisa de los datos expuestos a través del programa de divulgación responsable de Capital One.[2]
Capital One anuncia el incidente y el FBI detiene a una sospechosa en Seattle.[1]
Flujo de Ataque Paso a Paso
El firewall reenvía una petición manipulada
La denuncia penal alega que se podía inducir al WAF mal configurado a enviar peticiones a direcciones elegidas por el atacante, incluidas direcciones internas.[10]
Los datos salen en masa
Se copiaron datos de solicitudes de tarjetas de crédito desde 2005 hasta principios de 2019: unos 100 millones de personas en EE. UU. y 6 millones en Canadá, con unos 140.000 números de la Seguridad Social estadounidense y 80.000 números de cuentas bancarias vinculadas.[2]
Código Fuente: Vulnerable vs. Seguro
Código Flask ilustrativo; no es la configuración de Capital One. La lista permitida de hosts es el control principal de la aplicación. El proxy de salida obligatorio debe aplicar la política al destino resuelto en el momento de la conexión, bloquear rangos privados y link-local, incluido el servicio de metadatos, y repetir las comprobaciones en cada petición y reintento; verifica ese comportamiento en la configuración del proxy. No se siguen redirecciones. Una comprobación de resolución separada antes de conectar dejaría una condición de carrera de DNS rebinding.[9], [5], [6]
import requests
from flask import Flask, request
app = Flask(__name__)
@app.get("/fetch")
def fetch():
# El servidor obtiene cualquier URL que proporcione quien llama.
url = request.args["target"]
resp = requests.get(url, timeout=5)
return resp.text
from urllib.parse import urlsplit
import requests
from flask import Flask, abort, request
app = Flask(__name__)
ALLOWED_HOSTS = {"images.example-cdn.com", "api.partner.example"}
EGRESS_PROXY = "http://egress-proxy.internal:3128"
# Configure este proxy para aplicar la política de destino después de resolver DNS.
@app.get("/fetch")
def fetch():
parts = urlsplit(request.args.get("target", ""))
if parts.scheme != "https" or parts.hostname not in ALLOWED_HOSTS:
abort(400, "destination not allowed")
if parts.port not in (None, 443) or parts.username or parts.password:
abort(400, "destination not allowed")
resp = requests.get(parts.geturl(), timeout=(2, 5), allow_redirects=False, proxies={"https": EGRESS_PROXY}, trust_env=False)
if resp.is_redirect:
abort(502, "upstream redirect refused")
return resp.text
Lista de Verificación de Seguridad para Ingeniería
- ✓Envía las solicitudes de URL a través de un proxy de salida y aplica la lista permitida de hosts y la política de rangos IP en el límite de conexión: conecta solo con la dirección comprobada y aplica la política también a los reintentos. Desactiva las redirecciones o vuelve a validar cada salto; bloquear solo el texto 169.254.169.254 no basta.[9], [4]
- ✓Configura
HttpTokens=requireden cada instancia aplicable y comprueba las existentes, porque los valores predeterminados de la cuenta no actualizan automáticamente cada instancia. EligeHttpPutResponseHopLimitsegún la ruta de red de la carga de trabajo; AWS advierte que el valor 1 puede causar problemas en contenedores. La exigencia del token rechaza las peticiones IMDSv1; el límite de saltos es un ajuste de red independiente, no el control que bloquea un GET sin token.[6], [7] - ✓La denuncia penal alega que la ruta SSRF expuso el rol de la instancia. Limita ese rol a las acciones y recursos que necesita la carga de trabajo para que la exposición de credenciales no conceda acceso S3 ajeno.[10], [8]
- ✓Guía de detección de FlawOpen: alerta cuando se usen credenciales temporales del rol fuera de la red de la instancia e investiga con el contexto de la carga de trabajo y de la auditoría en la nube.
- ✓En la revisión de código, trata cualquier ruta en la que el usuario influya en una URL que pide el servidor igual que una en la que influya en una consulta SQL, y exige una prueba de la lista permitida.[9], [5]
Fuentes
- Departamento de Justicia de EE. UU. — detenida en Seattle una trabajadora tecnológica por robo de datos de una gran empresa financiera (29 de julio de 2019)
- Capital One — información sobre el incidente de ciberseguridad de 2019
- OCC — sanción civil a Capital One por el incidente de 2019 (agosto de 2020)
- ¿Por qué el SSRF es tan peligroso en entornos de nube?
- ¿Puede fetch() causar SSRF?
- AWS — Configure the Instance Metadata Service options
- AWS — Use the Instance Metadata Service to access instance metadata
- AWS — Retrieve security credentials from instance metadata
- OWASP — Server Side Request Forgery Prevention Cheat Sheet
- DOJ — United States v. Paige Thompson, criminal complaint (29 July 2019)