Saltar al contenido
Categoria: Pentest9 min de lectura

SSRF sin Complicaciones: Explotando Cloud Metadata en Lab AWS Local

Por Lucas Andrade ·

Reproduccion etica de SSRF contra IMDS con LocalStack, payloads reales, captura de credenciales simuladas y mitigacion definitiva con IMDSv2.

SSRF sin Complicaciones: Explotando Cloud Metadata en Lab AWS Local

El endpoint 169.254.169.254 quemo mas carreras de SRE que cualquier otra IP en la historia de la nube. Capital One en 2019 perdio cerca de 100 millones de registros porque una WAF mal configurada dejo que un Server-Side Request Forgery alcanzara el Instance Metadata Service y sacara credenciales temporales del rol de EC2. Anos despues seguimos recibiendo reportes de bug bounty con el patron identico: una app trae una imagen desde una URL provista por el usuario, nadie valida el destino, IMDSv1 queda activo, game over. Reconstruyamos ese ataque desde cero en un lab AWS local con LocalStack, entendamos exactamente por que IMDSv2 cambia la cuenta, y cerremos con un checklist de hardening que puedes desplegar hoy. Todo corre en tu propia maquina, asi que no hay mas objetivo que tu propio lab.

Que es SSRF y que hace el metadata service

Server-Side Request Forgery es una clase de bug donde un atacante controla el destino de una peticion que el servidor hace en su nombre. La aplicacion es el diputado confundido: tiene posicion de red e identidad que el atacante no tiene, y se conecta de buena gana adonde el apunte. En AWS, el destino interno de mayor valor es el Instance Metadata Service (IMDS) en 169.254.169.254, una direccion link-local que toda instancia EC2 alcanza. Expone datos de instancia y, critico, credenciales temporales de rol IAM bajo /latest/meta-data/iam/security-credentials/. Un SSRF que lo alcanza convierte un inocente fetch de imagen en robo de credenciales, por eso es el objetivo mas consecuente del pentest de nube.

Contexto de amenaza: por que esto sigue pasando

El patron se repite porque el codigo vulnerable se ve razonable. Un producto necesita traer un avatar, renderizar un preview de link o proxear un webhook, asi que acepta una URL y la llama del lado servidor. El desarrollador piensa en imagenes, no en un endpoint de metadata link-local que reparte credenciales de nube. Mientras tanto IMDSv1 quedo como default en instancias viejas por anos, asi que la boveda de credenciales estaba a una peticion sin validar de distancia. La misma forma aparece en Pentest de APIs REST y GraphQL: Checklist Tecnico para Bug Bounty Legal cuando endpoints de webhook o image-proxy aceptan URLs internas sin allowlist. Entender el patron es lo que te permite hallarlo en un objetivo y, mas importante, matarlo en tu propio codigo.

Armando el lab con LocalStack

Primero el ambiente. Levanta LocalStack Pro 4.x via docker-compose con ec2, iam, sts y s3 habilitados, mas un contenedor Flask expuesto en el puerto 5000 corriendo una API de fetch-image deliberadamente vulnerable. El codigo vulnerable son unas dieciocho lineas: recibe ?url=, llama a requests.get sin validacion y devuelve el body. Para simular IMDS dentro de LocalStack usas el mock de metadata de ec2 o un mock dedicado ligado a 169.254.169.254 via network namespace. Quien quiere maxima fidelidad usa una t3.micro real a un par de centavos por hora, pero LocalStack cubre cerca del 95% del aprendizaje sin tarjeta de credito. El setup base de contenedores que reutilizas aca esta cubierto en Pentest Web desde Cero: Montando un Lab Seguro con DVWA, Juice Shop y Burp Suite.

Explotando IMDSv1 paso a paso

Con el lab vivo, el payload clasico de IMDSv1 es literalmente un GET. Desde Burp, intercepta la peticion de la app y cambia el parametro url por http://169.254.169.254/latest/meta-data/iam/security-credentials/. La respuesta filtra el nombre del rol adjunto, digamos app-server-role. Luego pide http://169.254.169.254/latest/meta-data/iam/security-credentials/app-server-role y obtienes JSON con AccessKeyId, SecretAccessKey y Token. Exportalos como variables de entorno, corre aws sts get-caller-identity --endpoint-url http://localhost:4566, y estas autenticado como la aplicacion. Ese es el momento en que los blue teams saltan de sus sillas durante la demo, porque un unico parametro sin validar se acaba de convertir en identidad completa de rol.

Escalando con credenciales robadas

La escalada depende de lo que el rol pueda hacer. En nuestro lab adjuntamos una politica deliberadamente permisiva con s3:* y iam:ListRoles. Con las creds robadas, aws s3 ls revela un bucket backups-prod-2026, aws s3 cp s3://backups-prod-2026/db.dump . baja el dump, y aws iam list-attached-role-policies mapea el camino a escalada de privilegios via PassRole. Herramientas como Pacu, ScoutSuite y cloudfox automatizan esta enumeracion post-compromiso para que veas el radio de explosion rapido. Marca con timestamp cada comando, porque un reporte de bug bounty sin un PoC reproducible paga cero, y una linea de tiempo limpia es lo que convierte un hallazgo en un reporte triado y pagado. Si quieres profundizar en pivoting interno tras tomar credenciales, Pivoting con Chisel y Ligolo-ng: Redes Segmentadas en Lab de Pentest es la siguiente parada.

Por que IMDSv2 rompe el ataque

IMDSv2 rompe el ataque clasico exigiendo una sesion de token. El flujo correcto es un PUT a http://169.254.169.254/latest/api/token con header X-aws-ec2-metadata-token-ttl-seconds: 21600, luego un GET llevando el header X-aws-ec2-metadata-token. Un SSRF que solo hace un GET, sin control sobre metodo ni headers, simplemente se estanca: no puede obtener un token, asi que no puede leer credenciales. Fuerza IMDSv2 con aws ec2 modify-instance-metadata-options --http-tokens required --http-put-response-hop-limit 1. El hop-limit de 1 es crucial: impide que contenedores en red bridge alcancen IMDS via el NAT del host, cerrando un camino comun de contenedor a metadata. Combina eso con un bloqueo de egress de 169.254.0.0/16 en el security group y la app pierde toda ruta al endpoint magico.

Defensa en profundidad mas alla de IMDS

La defensa no termina en IMDS. Valida URLs en la aplicacion, rechazando 169.254.0.0/16, 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 y fd00::/8, y resuelve el hostname antes de la peticion para derrotar DNS rebinding, luego fija esa IP resuelta para la conexion real. Usa una libreria auditada como ssrf-protect o implementalo con getaddrinfo mas chequeos de IP por familia; una blocklist de strings ingenua se evade trivialmente con IPs decimales, direcciones IPv6-mapeadas o redirects. Recorta los permisos del rol al minimo, prefiere IRSA en EKS para que los pods reciban credenciales de vida corta y acotadas, habilita GuardDuty para uso anomalo de credenciales, y corre scanners como Prowler regularmente. Tecnicas web relacionadas viven en SQL Injection en la Practica: Explotar, Detectar y Mitigar en Lab Controlado y XSS Moderno: DOM, Stored y Reflected con Ejemplos Reales en Entorno de Pruebas, que junto con SSRF forman el trio mas comun en bug bounty corporativo.

Deteccion y monitoreo

Asume que la prevencion fallara de vez en cuando e instrumenta para deteccion. El robo de credenciales via IMDS tiene una senal: el mismo rol IAM de pronto autenticando desde una IP fuera de tu infraestructura. El finding UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration de GuardDuty esta hecho justo para eso, asi que habilitalo y enrutalo a un canal de guardia. En la capa de aplicacion, loguea cada URL saliente que el fetcher resuelve y alerta ante cualquier peticion cuyo destino caiga en un rango privado o link-local, porque un fetch legitimo de avatar nunca apunta a 169.254.169.254. Alimenta esos logs al mismo pipeline que usas para otras detecciones para que un intento de SSRF se vuelva una alerta que suena en vez de una linea que nadie lee.

Errores comunes

Vale nombrar los errores recurrentes. Los equipos bloquean el string literal 169.254.169.254 y pierden 2852039166, su forma decimal, o un hostname controlado por el atacante que resuelve a el. Validan la URL una vez, luego siguen redirects que apuntan derecho de vuelta al metadata service. Fuerzan IMDSv2 en instancias nuevas pero dejan una cola larga de viejas en optional. Ponen hop-limit pero olvidan la regla de egress, o al reves. Y le dan al rol de instancia mucho mas de lo que necesita, asi que hasta una filtracion breve de credenciales se vuelve un compromiso total de cuenta. La defensa en profundidad existe justamente porque cualquiera de estos fallara con el tiempo.

Checklist de hardening

Despliega esto hoy: corre aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens]' sobre tu inventario ahora, y cualquier instancia que devuelva optional esta expuesta al SSRF clasico; fuerza required en masa via un SSM Automation Document; pon hop-limit en 1; agrega un bloqueo de egress para 169.254.0.0/16 donde sea factible; audita roles con politicas *:* y recortalas; valida y fija URLs salientes en la app con una libreria auditada; habilita GuardDuty y enruta el finding de exfiltracion a guardia; y agrega un test E2E en CI que intente alcanzar 169.254.169.254 desde el contenedor de la app y falle el build ante una respuesta 200.

FAQ: Basta IMDSv2 solo para detener SSRF?

IMDSv2 derrota el camino especifico de SSRF a robo de credenciales cuando el SSRF se limita a GETs simples, lo que cubre la gran mayoria de los casos reales. No es un fix completo de SSRF. Un atacante que puede controlar metodos y headers, o que pivotea a otros servicios internos ademas de IMDS, todavia tiene margen. Trata IMDSv2 como un control obligatorio y de alto valor, y agrega validacion de URL, roles de minimo privilegio y restricciones de egress para que el metadata service no sea tu unica linea de defensa.

FAQ: Puedo practicar esto seguro sin factura de AWS?

Si. LocalStack Pro te da EC2, IAM, STS y S3 mas un mock de metadata, que reproduce cerca del 95% del aprendizaje sin gasto de nube y sin riesgo de tocar nada que no sea tuyo. Manten la app Flask vulnerable y el mock de metadata estrictamente en una red docker local. Cuando eventualmente quieras maxima fidelidad para un edge case especifico, una t3.micro real cuesta un par de centavos por hora, pero nunca apuntes nada de este tooling a infraestructura que no estas autorizado a testear.

Conclusion

Takeaway practico: toma unos diecisiete minutos reproducir el patron de Capital One en el lab, y toma los mismos diecisiete minutos cerrar el hueco en produccion. Corre la query de inventario ahora, fuerza IMDSv2 en todos lados, recorta los roles sobre-permisivos, valida URLs salientes en tus apps, y agrega el test de CI que falla el build si el contenedor de la app alcanza 169.254.169.254. El SSRF contra metadata de nube no es exotico; es un problema de configuracion por defecto con un fix bien entendido. Los equipos que sufren brechas no son los que carecian del conocimiento, sino los que nunca corrieron la query.

Related posts

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly