DevOps
🛡️ IMDSv1 vs IMDSv2 en EC2: SSRF, métricas, riesgos y una migración segura
Terry Quispe Paniagua Dev.to (EN Zone)
2 views
¿Qué riesgo existe realmente al mantener IMDSv1 habilitado en una instancia EC2?
En 2019, el incidente de Capital One mostró que el acceso no autorizado al EC2 Instance Metadata Service (IMDS) puede formar parte de una cadena de ataque con consecuencias muy serias.
El incidente terminó afectando información de más de 100 millones de personas.
Más adelante veremos qué ocurrió y qué papel tuvo el metadata service.
Pero primero necesitamos entender una pieza que muchas instancias EC2 todavía pueden tener habilitada:
IMDSv1.
Migrar parece sencillo (a IMDSv2). En una instancia existente, desde la consola de EC2 podemos modificar las opciones de metadata y cambiar:
La propia consola muestra una advertencia importante antes de hacer el cambio:
Asegúrese de que la instancia no realiza llamadas a IMDSv1 antes de configurar IMDSv2 en obligatorio.
AWS incluso nos indica revisar la métrica MetadataNoToken de CloudWatch.
Esa advertencia resume perfectamente el problema que vamos a probar en este laboratorio.
Mantener IMDSv1 representa un riesgo de seguridad, pero exigir IMDSv2 sin revisar primero las dependencias puede romper aplicaciones, agentes, SDKs o scripts antiguos.
En este laboratorio vamos a revisar ambos lados:
La estrategia será:
¿Qué es EC2 Instance Metadata Service?
Amazon EC2 Instance Metadata Service, conocido como IMDS, permite que software ejecutándose dentro de una instancia EC2 consulte información sobre la propia instancia.
IMDS utiliza la dirección link-local:
169.254.169.254
Por ejemplo, mientras IMDSv1 está permitido podemos consultar directamente distintos datos de la instancia.
AMI ID
curl http://169.254.169.254/latest/meta-data/ami-id
Tipo de instancia
curl http://169.254.169.254/latest/meta-data/instance-type
Hostname
curl http://169.254.169.254/latest/meta-data/hostname
IAM role
Si la instancia tiene un instance profile asociado:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
En nuestro laboratorio debería devolver:
IMDSLabRole
Credenciales temporales IAM
El nombre del role forma parte del siguiente path:
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE"
La respuesta incluye las credenciales temporales asociadas al instance profile:
{
"Code": "Success",
"AccessKeyId": "ASIA...REDACTED",
"SecretAccessKey": "REDACTED",
"Token": "REDACTED",
"Expiration": "..."
}
Cuando una instancia tiene un IAM role asociado mediante un instance profile, las aplicaciones pueden obtener credenciales temporales desde IMDS sin almacenar access keys dentro del servidor.
Conceptualmente:
El bucket se muestra únicamente como ejemplo. Podría tratarse de cualquier recurso o servicio sobre el que el role tenga permisos.
Este mecanismo es completamente normal y forma parte del funcionamiento de muchos workloads sobre EC2.
El problema aparece cuando analizamos cómo se accede al metadata service.
IMDSv1 vs IMDSv2: ¿qué cambia realmente?
Con IMDSv1, consultar metadata es básicamente una petición HTTP directa.
Por ejemplo:
curl http://169.254.169.254/latest/meta-data/instance-id
El flujo es muy simple:
Aplicación
│
│ GET
▼
169.254.169.254
│
▼
Metadata
No existe ningún token previo.
IMDSv2 cambia este comportamiento agregando una sesión basada en token.
Primero debemos solicitar el token:
TOKEN=$(curl -sS -X PUT \
"http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
Luego utilizamos ese token en las consultas:
curl -sS \
-H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-id
El flujo cambia a:
Consola vs API, CLI y Terraform
Aquí existe una diferencia de nombres que puede generar confusión.
Cuando modificamos una instancia existente desde la consola, AWS nos muestra:
IMDSv2 = Opcional
o:
IMDSv2 = Obligatorio
En cambio, mediante API, AWS CLI, SDKs o Infrastructure as Code, esta misma configuración se representa mediante HttpTokens.
La equivalencia es:
No son configuraciones diferentes.
Son dos formas de representar la misma opción de EC2.
Al lanzar una instancia nueva, la consola también puede mostrar esta configuración como Metadata version, por ejemplo V2 only (token required).
Esta se encuentra en la sección de detalles avanzados al final de las configuraciones.
¿Qué es el límite de saltos de respuesta HTTP PUT?
En la misma pantalla de configuración aparece:
Límite de saltos de respuesta HTTP PUT
Esta opción está relacionada específicamente con el mecanismo de sesión de IMDSv2.
Recordemos que primero solicitamos un token:
El hop limit determina cuántos saltos de red puede recorrer la respuesta que contiene ese token.
AWS permite valores entre 1 - 64
En un escenario simple como nuestro laboratorio:
un valor de 1 es suficiente. Sin embargo, con contenedores puede existir una capa adicional.
En ese escenario, un hop limit de 1 puede impedir que la respuesta con el token llegue correctamente al proceso dentro del contenedor.
Por eso AWS recomienda considerar:
cuando IMDSv2 se utiliza desde entornos de contenedores.
En nuestro laboratorio utilizamos:
http_put_response_hop_limit = 1
porque la aplicación se ejecuta directamente sobre la instancia EC2.
Es importante no confundir ambos parámetros:
¿Por qué IMDSv1 sigue representando un riesgo de seguridad?
IMDSv1 no es por sí mismo una vulnerabilidad.
El problema aparece cuando otra vulnerabilidad permite que un atacante consiga realizar peticiones desde la propia instancia.
Por ejemplo:
Si IMDSv1 está disponible, una petición HTTP sin token puede ser suficiente para consultar metadata.
Y si la instancia tiene un IAM role:
El impacto posterior dependerá completamente de los permisos asignados a ese role.
Obtener credenciales temporales de un role con permisos mínimos no equivale a comprometer toda una cuenta AWS.
Por eso este escenario también demuestra la importancia de aplicar IAM least privilege.
¿Qué es SSRF y por qué importa aquí?
Uno de los escenarios que puede permitir alcanzar IMDS es Server-Side Request Forgery, normalmente abreviado como SSRF.
La idea es relativamente sencilla.
Imaginemos una aplicación que permite recibir una URL para consultar su contenido.
Por ejemplo:
/fetch?url=https://example.com
Desde nuestro equipo podríamos llamar:
curl "http://PUBLIC_IP:8080/fetch?url=https://example.com"
Aquí existen realmente dos destinos diferentes.
El primero:
http://PUBLIC_IP:8080/fetch
es nuestra aplicación ejecutándose en EC2.
El segundo:
url=https://example.com
es un parámetro que indica qué URL debe consultar la aplicación desde el servidor.
El flujo normal sería:
Hasta aquí no existe ningún problema.
El riesgo aparece cuando la aplicación permite controlar libremente el valor de url.
En lugar de:
url=https://example.com
podemos indicar:
url=http://169.254.169.254/latest/meta-data/instance-id
La petición completa queda así:
curl "http://PUBLIC_IP:8080/fetch?url=http://169.254.169.254/latest/meta-data/instance-id"
La parte crítica está después de ?url=:
http://PUBLIC_IP:8080/fetch
│
└── Aplicación vulnerable ejecutándose en EC2
?url=
│
└── Parámetro controlado por quien realiza la petición
http://169.254.169.254/latest/meta-data/instance-id
│
└── URL que le estamos indicando a la EC2 que consulte
Nuestro equipo no está accediendo directamente al metadata service mediante:
169.254.169.254
Estamos enviando una petición a la aplicación y consiguiendo que sea la propia EC2 quien realice una segunda petición.
El flujo real es:
Ese es el punto clave del SSRF:
la petición hacia el recurso interno la realiza el servidor, no el atacante directamente.
Esto también explica por qué una instancia EC2 no necesita tener una IP pública para que este riesgo exista.
Podríamos tener perfectamente:
La aplicación puede ser accesible mediante un ALB mientras la instancia continúa teniendo acceso local a IMDS.
SSRF está clasificado como:
CWE-918
Server-Side Request Forgery
MITRE ATT&CK también contempla específicamente el acceso al metadata service de proveedores cloud:
Esto nos ayuda a separar correctamente los conceptos:
SSRF
│
│ vulnerabilidad / vector de ataque
▼
Instance Metadata
│
▼
Credenciales
IMDSv2 no corrige una vulnerabilidad SSRF.
Lo que hace es agregar una capa adicional de protección para dificultar determinados caminos hacia el metadata service.
Un caso real: Capital One
En 2019, Capital One sufrió uno de los incidentes más conocidos relacionados con este tipo de cadena de ataque.
Una debilidad en el procesamiento de determinadas peticiones permitió alcanzar recursos internos.
Desde allí fue posible acceder al EC2 Instance Metadata Service y obtener credenciales temporales asociadas a un IAM role.
Esas credenciales fueron utilizadas posteriormente para acceder a información almacenada en S3.
El incidente afectó información de más de 100 millones de personas.
Es importante no simplificar el incidente diciendo:
Capital One fue comprometido únicamente por utilizar IMDSv1.
Eso sería incorrecto.
Existió una cadena de condiciones:
IMDSv1 fue una pieza dentro de esa cadena.
IMDSv2 todavía no existía cuando ocurrió el incidente. AWS lo lanzó posteriormente en 2019 como una mejora de defensa en profundidad para EC2 Instance Metadata Service.
Hoy tenemos esa protección disponible.
Por eso la pregunta ya no debería ser si conviene mantener IMDSv1.
La pregunta es:
¿Cómo migramos a IMDSv2 sin romper los workloads que todavía dependen de IMDSv1?
Eso es precisamente lo que vamos a probar.
Arquitectura del laboratorio
Quiero mantener este laboratorio deliberadamente pequeño para concentrarnos en el comportamiento de IMDS y no en componentes adicionales.
Toda la infraestructura se despliega con Terraform.
⚠️ Disclaimer del laboratorio
Este laboratorio despliega intencionalmente una aplicación vulnerable a SSRF con fines educativos.
Ejecútalo únicamente en una cuenta o entorno AWS que controles o para el que tengas autorización explícita.
El Security Group limita el acceso al puerto 8080 a tu propia IP y el IAM role utilizado en el laboratorio no necesita permisos sobre recursos reales.
Durante las pruebas podremos consultar credenciales temporales desde IMDS para demostrar el riesgo. No publiques AccessKeyId, SecretAccessKey ni Token reales, aunque sean temporales.
Al finalizar las pruebas, revisa la sección Limpieza del laboratorio para eliminar los recursos y restaurar las configuraciones regionales modificadas durante el laboratorio.
El código está disponible aquí:
GitHub: https://github.com/RedCaraxes/aws-imdsv1-to-imdsv2-lab
Terraform crea:
Internet
│
│ TCP/8080
│ Source: My IP
▼
Public Subnet
│
▼
EC2 t3.nano
Amazon Linux 2023
│
├── IMDSLabRole
├── Aplicación vulnerable de prueba
│
└── Instance Metadata Service
CloudWatch Dashboard
├── MetadataNoToken
└── MetadataNoTokenRejected
El laboratorio incluye una VPC, una subnet pública, una instancia EC2 t3.nano, un Security Group que permite acceso al puerto 8080 únicamente desde nuestra IP y un IAM role llamado:
IMDSLabRole
El role no necesita permisos sobre recursos reales.
El objetivo es demostrar que el metadata service puede exponer credenciales temporales asociadas al instance profile, no utilizar esas credenciales contra otros servicios.
La aplicación de prueba se instala automáticamente mediante user_data y expone:
/health
/legacy
/v2
/fetch?url=
Cada endpoint tiene un propósito:
/health
└── validar que la aplicación está disponible
/legacy
└── realizar una consulta IMDSv1 sin token
/v2
└── obtener un token y consultar IMDSv2 correctamente
/fetch?url=
└── realizar una petición hacia una URL controlada
por el usuario para demostrar SSRF
La instancia comienza con:
Consola:
IMDSv2 = Opcional
Terraform:
http_tokens = "optional"
Resultado:
IMDSv1 ✅
IMDSv2 ✅
También configuramos:
http_put_response_hop_limit = 1
porque la aplicación se ejecuta directamente sobre la instancia y no dentro de un contenedor.
La IP pública existe únicamente para simplificar el laboratorio.
Como vimos anteriormente, una arquitectura real podría mantener la instancia en una subnet privada detrás de un Application Load Balancer.
Costo estimado del laboratorio
Este laboratorio utiliza recursos con costo.
En us-east-1, manteniendo la infraestructura desplegada durante aproximadamente 24 horas, el costo debería estar alrededor de:
EC2 t3.nano ~ $0.125
IPv4 pública ~ $0.120
EBS gp3 8 GiB ~ $0.021
--------------------------------
Total aproximado ~ $0.27 / día
El tráfico generado por las pruebas es mínimo y las métricas MetadataNoToken y MetadataNoTokenRejected son métricas nativas de EC2.
El dashboard de CloudWatch puede quedar cubierto por la cuota gratuita de hasta tres dashboards de 50 métricas cada uno. Si la cuenta ya supera esa cuota, aplicará el precio estándar de CloudWatch.
El valor puede variar según la región, el tiempo que mantengas los recursos desplegados y las condiciones de facturación de tu cuenta.
Por eso, al terminar las pruebas recuerda ejecutar la sección Limpieza del laboratorio.
Levantando el laboratorio
Clonamos el repositorio:
git clone https://github.com/RedCaraxes/aws-imdsv1-to-imdsv2-lab.git
cd aws-imdsv1-to-imdsv2-lab
Creamos nuestro archivo de variables:
cp terraform.tfvars.example terraform.tfvars
Configuramos nuestra IP pública:
allowed_cidr = "YOUR_PUBLIC_IP/32"
Y mantenemos inicialmente:
http_tokens = "optional"
Desplegamos:
terraform init
terraform plan
terraform apply
Luego obtenemos la IP pública:
export PUBLIC_IP=$(terraform output -raw public_ip)
Podemos verificarla:
echo "$PUBLIC_IP"
Y validar que la aplicación está funcionando:
curl "http://$PUBLIC_IP:8080/health"
La respuesta debería ser:
ok
Estado inicial: IMDSv1 e IMDSv2 habilitados
Como configuramos IMDSv2 como Opcional, nuestra instancia acepta inicialmente ambos mecanismos:
IMDSv1 ✅
IMDSv2 ✅
Primero comprobaremos ambos comportamientos antes de probar el escenario SSRF.
Validando IMDSv1
El endpoint:
/legacy
realiza una petición hacia IMDS sin utilizar token.
Ejecutamos:
curl -i "http://$PUBLIC_IP:8080/legacy"
La respuesta debería devolver el instance-id:
HTTP/1.0 200 OK
i-xxxxxxxxxxxxxxxxx
Esto demuestra que una petición sin token todavía puede acceder al metadata service.
Validando IMDSv2
Ahora usamos:
/v2
Este endpoint primero obtiene correctamente un token y luego consulta IMDS utilizando ese token.
Ejecutamos:
curl -i "http://$PUBLIC_IP:8080/v2"
La respuesta devuelve el mismo instance-id.
Conceptualmente:
Aplicación
│
│ PUT /latest/api/token
▼
IMDS
│
│ Token
▼
Aplicación
│
│ GET instance-id + token
▼
IMDS
Tenemos entonces:
IMDSv1 ✅
IMDSv2 ✅
Probando primero un destino normal
Antes de apuntar hacia IMDS, comprobemos el comportamiento normal del endpoint vulnerable.
Ejecutamos:
curl "http://$PUBLIC_IP:8080/fetch?url=https://example.com"
El flujo es:
Nuestro equipo
│
▼
Aplicación EC2
│
▼
example.com
La aplicación recibe el valor de:
url=
y realiza la petición desde la instancia.
Ahora cambiaremos únicamente ese valor.
Probando IMDSv1 mediante SSRF
Intentemos consultar el instance-id:
curl "http://$PUBLIC_IP:8080/fetch?url=http://169.254.169.254/latest/meta-data/instance-id"
Recordemos qué significa esta petición:
http://$PUBLIC_IP:8080/fetch
│
└── endpoint de nuestra aplicación
?url=
│
└── parámetro que controla el destino
http://169.254.169.254/latest/meta-data/instance-id
│
└── URL que la aplicación consultará desde la EC2
Por tanto:
Nuestro equipo
│
│ solicita /fetch
▼
Aplicación vulnerable
│
│ consulta el valor enviado en ?url=
▼
169.254.169.254
│
▼
Instance ID
La respuesta devuelve:
i-xxxxxxxxxxxxxxxxx
Ya demostramos que nuestra aplicación vulnerable puede alcanzar IMDSv1.
Accediendo al metadata del IAM role
Podemos consultar qué IAM role está asociado cambiando únicamente el destino enviado mediante url=:
curl "http://$PUBLIC_IP:8080/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
La aplicación recibe:
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
y realiza esa petición desde la propia instancia.
La respuesta debería mostrar:
IMDSLabRole
Existe otro endpoint debajo de ese path que contiene las credenciales temporales utilizadas por la instancia.
Una respuesta tendría campos similares a:
{
"Code": "Success",
"AccessKeyId": "ASIA...REDACTED",
"SecretAccessKey": "REDACTED",
"Token": "REDACTED"
}
Nunca publiques esos valores reales.
En nuestro laboratorio no necesitamos utilizar esas credenciales contra otros recursos (aunque ya evidenciamos eso antes).
Ya tenemos la evidencia necesaria:
SSRF
↓
IMDSv1
↓
Metadata del IAM role
Antes de migrar: revisar MetadataNoToken
Aquí retomamos la advertencia que vimos al inicio en la consola de EC2.
Antes de cambiar IMDSv2 de:
Opcional
a:
Obligatorio
AWS recomienda comprobar si todavía existen llamadas IMDSv1.
¿Cómo podemos saberlo?
EC2 publica de forma nativa en CloudWatch la métrica:
MetadataNoToken
No necesitamos instalar CloudWatch Agent ni crear una métrica personalizada.
MetadataNoToken registra llamadas al metadata service realizadas sin token mientras IMDSv1 todavía está permitido.
Nuestro laboratorio ya debería estar generándolas.
Terraform crea un dashboard de CloudWatch para visualizar:
MetadataNoToken
MetadataNoTokenRejected
Es importante aclarar que Terraform no crea ni habilita estas métricas.
Son métricas nativas de EC2.
El dashboard existe únicamente para hacer más cómoda la visualización durante la transición.
Después de nuestras pruebas observamos:
MetadataNoToken > 0
Eso confirma que existen llamadas a IMDS sin token.
En nuestro laboratorio sabemos exactamente de dónde vienen.
En producción podría tratarse de:
script legacy
AWS SDK antiguo
agente de monitoreo
aplicación
proceso de bootstrap
user-data
herramientas personalizadas
Por eso:
MetadataNoToken > 0
significa que no deberíamos exigir IMDSv2 todavía sin investigar.
¿Cuánto tiempo deberíamos observar la métrica?
Ver:
MetadataNoToken = 0
durante cinco minutos no demuestra necesariamente que el workload ya no depende de IMDSv1.
Un proceso podría ejecutarse:
cada hora
diariamente
durante un deployment
durante el arranque de la instancia
durante un backup
durante un failover
El período de observación debe representar el comportamiento real del workload.
Conceptualmente:
MetadataNoToken > 0
│
▼
Identificar dependencia
│
▼
Actualizar aplicación / SDK / script
│
▼
Observar nuevamente
│
▼
MetadataNoToken = 0
durante un período representativo
Recién entonces podemos avanzar.
Simulando una dependencia legacy
En nuestro laboratorio:
/legacy
representa un software antiguo que consulta IMDS sin token.
Ejecutamos:
curl "http://$PUBLIC_IP:8080/legacy"
Mientras IMDSv2 esté configurado como Opcional, el endpoint funciona.
Conceptualmente está haciendo algo equivalente a:
curl http://169.254.169.254/latest/meta-data/instance-id
Una implementación compatible con IMDSv2 necesita primero obtener un token:
TOKEN=$(curl -sS -X PUT \
"http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
y posteriormente utilizarlo:
curl -sS \
-H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/instance-id
En una migración real aquí deberíamos actualizar las dependencias que todavía utilizan IMDSv1:
scripts
SDKs
agentes
aplicaciones
hasta eliminar las llamadas sin token.
Exigiendo IMDSv2
Una vez identificadas y corregidas las dependencias podemos realizar el cambio que vimos al inicio del artículo.
En la consola:
IMDSv2
Opcional
↓
Obligatorio
Mediante API o AWS CLI:
HttpTokens
optional
↓
required
Y en nuestro Terraform cambiamos (terraform.tfvars):
http_tokens = "optional"
por:
http_tokens = "required"
Aplicamos:
terraform plan
terraform apply
La modificación se aplica in-place sobre la instancia.
No requiere reemplazarla ni reiniciarla, y EC2 continúa ejecutándose durante el cambio.
Sin embargo, eso no significa que el cambio sea transparente para el workload.
En cuanto HttpTokens = required queda aplicado, cualquier aplicación, agente o script que todavía dependa de IMDSv1 comenzará a recibir rechazos.
Ahora el comportamiento esperado es:
IMDSv1 ❌
IMDSv2 ✅
¿Qué podría haberse roto?
Después del cambio volvemos a ejecutar:
curl -i "http://$PUBLIC_IP:8080/legacy"
El endpoint intenta realizar la misma llamada sin token.
Pero IMDSv1 ya no está permitido.
Nuestro pequeño workload legacy deja de funcionar.
Ahora imaginemos que ese mismo comportamiento formara parte de:
inicio de una aplicación
deployment
monitoreo
obtención de credenciales
automatización
El cambio de seguridad podría convertirse directamente en un incidente.
Por eso:
Un cambio hacia una configuración más segura todavía puede convertirse en una migración insegura.
El objetivo no es solamente activar IMDSv2.
El objetivo es hacerlo sin romper los workloads.
Validando que IMDSv2 continúa funcionando
Después de exigir IMDSv2 repetimos:
curl -i "http://$PUBLIC_IP:8080/v2"
Este endpoint obtiene primero un token de IMDSv2 y lo utiliza para consultar el metadata service.
Después de configurar HttpTokens = required, la petición continúa devolviendo correctamente el instance-id.
Esto confirma un punto importante:
exigir IMDSv2 no deshabilita Instance Metadata Service.
Lo que cambia es el mecanismo de acceso. /v2 obtiene el token y lo utiliza en la petición hacia IMDS, mientras que los clientes que intentan acceder sin token son rechazados.
Conceptualmente:
IMDSv1 ❌
IMDSv2 ✅
El problema no es el metadata service.
El problema son los clientes que todavía intentan acceder sin token.
Probando nuevamente el mismo SSRF
Ahora repetimos exactamente la misma petición utilizada antes:
curl "http://$PUBLIC_IP:8080/fetch?url=http://169.254.169.254/latest/meta-data/instance-id"
La aplicación vulnerable sigue recibiendo:
url=http://169.254.169.254/latest/meta-data/instance-id
y sigue intentando realizar:
GET http://169.254.169.254/latest/meta-data/instance-id
Pero ahora IMDS exige primero:
PUT /latest/api/token
y posteriormente espera:
X-aws-ec2-metadata-token
La misma SSRF simple que anteriormente podía consultar metadata deja de funcionar.
Conceptualmente:
Pero esto no significa que hayamos corregido nuestra aplicación.
La aplicación sigue teniendo SSRF.
IMDSv2 agrega defensa en profundidad alrededor del metadata service.
La vulnerabilidad de la aplicación también debe ser corregida.
Después de migrar: MetadataNoTokenRejected
Cuando IMDSv2 está configurado como:
Obligatorio
la configuración correspondiente es:
HttpTokens = required
Las llamadas IMDSv1 ya no son aceptadas.
Aquí aparece otra métrica nativa de EC2:
MetadataNoTokenRejected
Esta métrica registra intentos de acceder al metadata service sin token después de que IMDSv2 pasó a ser obligatorio.
Nuestra prueba:
curl "http://$PUBLIC_IP:8080/legacy"
genera precisamente este tipo de intento.
La prueba SSRF también continúa intentando llegar a IMDS sin token.
Conceptualmente:
En CloudWatch deberíamos observar:
MetadataNoTokenRejected > 0
Esto nos da observabilidad en ambos lados del cambio:
ANTES DE LA MIGRACIÓN
¿Todavía existen llamadas IMDSv1?
│
▼
MetadataNoToken
DESPUÉS DE LA MIGRACIÓN
¿Algo todavía intenta utilizar IMDSv1?
│
▼
MetadataNoTokenRejected
Evitando que IMDSv1 vuelva a aparecer
Ya migramos nuestra instancia.
Pero todavía existe otro problema.
Nada evita necesariamente que alguien lance mañana otra EC2 permitiendo nuevamente IMDSv1.
Por eso el último paso debe ser evitar regresiones.
A nivel de instancia
Para una instancia específica:
HttpTokens = required
es suficiente para exigir IMDSv2 en ese recurso.
Pero administrar esto instancia por instancia no escala.
Valores predeterminados a nivel de cuenta
Podemos establecer IMDSv2 como valor predeterminado regional:
aws ec2 modify-instance-metadata-defaults \
--region us-east-1 \
--http-tokens required
Podemos comprobar la configuración con:
aws ec2 get-instance-metadata-defaults \
--region us-east-1
Pero existe una diferencia importante:
Un valor predeterminado no necesariamente implica que la configuración sea obligatoria.
Un parámetro especificado explícitamente durante el lanzamiento puede tener mayor precedencia que el valor predeterminado de cuenta.
Exigir IMDSv2 a nivel de cuenta
AWS también permite exigir el comportamiento:
aws ec2 modify-instance-metadata-defaults \
--region us-east-1 \
--http-tokens required \
--http-tokens-enforced enabled
Podemos verificarlo:
aws ec2 get-instance-metadata-defaults \
--region us-east-1
El resultado debería incluir algo similar a:
{
"AccountLevel": {
"HttpTokens": "required",
"ManagedBy": "account",
"HttpTokensEnforced": "enabled"
}
}
Con enforcement habilitado, una nueva instancia no puede lanzarse si el resultado fuera permitir IMDSv1.
Pero existe un detalle importante:
habilitar enforcement a nivel de cuenta no migra automáticamente las instancias existentes que todavía tienen HttpTokens = optional.
Esas instancias deben analizarse y migrarse.
Desde la consola también podemos configurarlo desde EC2 → Atributos de la cuenta.
Resultado final
Comenzamos con:
Aplicación
│
│ SSRF
▼
IMDSv1
│
▼
Metadata del IAM role
Pero también vimos que exigir IMDSv2 sin revisar previamente nuestras dependencias puede romper software legítimo.
Por eso una migración controlada debería seguir:
La recomendación final no es simplemente:
El objetivo no es solamente terminar con una configuración más segura.
El objetivo es llegar a ella sin convertir el hardening en el siguiente incidente.
Limpieza del laboratorio
Al terminar las pruebas eliminamos primero los recursos administrados por Terraform:
terraform destroy
Durante el laboratorio también modificamos los defaults de IMDS a nivel de cuenta y región mediante AWS CLI.
Esos cambios no forman parte del estado de Terraform, por lo que debemos restaurarlos por separado.
Si antes del laboratorio no teníamos valores explícitos configurados, podemos volver a no-preference:
aws ec2 modify-instance-metadata-defaults \
--region us-east-1 \
--http-tokens no-preference \
--http-tokens-enforced no-preference
Verificamos el resultado:
aws ec2 get-instance-metadata-defaults \
--region us-east-1
Podríamos volver a obtener una respuesta como:
{
"AccountLevel": {
"ManagedBy": "account"
}
}
Si la cuenta ya tenía una configuración específica antes del laboratorio, restaura esos valores originales en lugar de utilizar no-preference.
Referencias
AWS - Instance metadata and user data
AWS - Configure the Instance Metadata Service options
AWS - Modify instance metadata options for existing instances
AWS - Configure instance metadata options for new instances
AWS - Transition to using Instance Metadata Service Version 2
MITRE ATT&CK - T1552.005: Cloud Instance Metadata API
MITRE CWE-918: Server-Side Request Forgery
U.S. Department of Justice - Capital One incident
AWS - Amazon EC2 On-Demand Pricing
AWS - Amazon VPC Pricing
AWS - Amazon EBS Pricing
AWS - Amazon CloudWatch Pricing
Read original: https://dev.to/terry_cloud/imdsv1-vs-imdsv2-en-ec2-ssrf-metricas-riesgos-y-una-migracion-segura-4j62
Related
Redundancy in Software Systems: What It Is, How It Works, and When to Use It
DevOps
0
DEV Community
I run my homelab like a small company: the architecture, the org chart, and the rules I only learned by breaking things
DevOps
0
DEV Community
I Wrapped My x402 Pay-Per-Call APIs in an MCP Server — and Got Into the Official Registry Without GitHub
DevOps
2
Dev.to (EN Zone)
If you're just about to switch to Linux, read this.
DevOps
2
DEV Community
Comments0
No comments yet — be the first