The instance metadata service (IMDS) is the mechanism by which an EC2 instance fetches its own IAM credentials. When IMDSv1 is reachable, any attacker who can make the instance perform an HTTP request of their choosing — SSRF, a misconfigured proxy, a vulnerable image renderer — can read the instance’s role credentials and use them from anywhere on the internet.
IMDSv2 closes this. It requires a session token obtained via a PUT request, sets a default TTL that SSRF vectors cannot meet, and is enforced at the instance level. And yet, five years after its release, the majority of production AWS accounts we audit still have IMDSv1 reachable on at least one running instance.
Critical
If any instance in your account still allows IMDSv1, assume one SSRF bug away from a credential leak. There is no workload that requires IMDSv1 that cannot be updated.
Why it stays this way
Three reasons, roughly ranked:
-
“Migration” framing. Teams treat IMDSv2 enforcement as a per-workload task. That produces a spreadsheet, a backlog, and a year of delay. The correct framing is an account-level default: everything new is IMDSv2-only, and the existing set is closed in one sweep.
-
Fear of breaking old images. Some legacy AMIs or pre-2019 SDKs could not obtain the v2 session token. That has been resolved in every current AWS SDK for at least four years. If you are still running a 2018 Python SDK in production, the IMDS issue is the smaller problem.
-
Confusion between “enforcing IMDSv2” and “disabling IMDS entirely.” The required action is the former. The hop limit and token requirement are the controls; IMDS itself remains available.
The three account-level settings that close this
1. Default IMDSv2 at the account level
aws ec2 modify-instance-metadata-defaults \
--region us-east-1 \
--http-tokens required \
--http-put-response-hop-limit 2 \
--http-endpoint enabled
Apply per region. Every new instance launched in the account inherits these defaults. No Terraform change. No per-workload override.
2. Remediate existing instances in one pass
aws ec2 describe-instances \
--filters "Name=metadata-options.http-tokens,Values=optional" \
--query 'Reservations[].Instances[].InstanceId' \
--output text \
| xargs -n1 -I{} aws ec2 modify-instance-metadata-options \
--instance-id {} --http-tokens required --http-put-response-hop-limit 2
The command is idempotent and does not require instance restart. The maximum-blast-radius version of this — running it against all instances simultaneously — is safer than the incremental version, because incremental means some instances stay exposed longer for no gain.
3. Enforce via SCP
Once the account is clean, lock it:
{
"Sid": "DenyIMDSv1Launch",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": { "ec2:MetadataHttpTokens": "required" }
}
}
This prevents any future launch — from any user, role, or pipeline — from regressing.
What attackers actually do with IMDSv1
The attack is not theoretical. In the public-record cloud breaches of the last five years, a significant fraction included a step where an application endpoint was induced to make a request to 169.254.169.254. The request returns the role credentials. The credentials are valid from the attacker’s laptop. From that role, further escalation follows the trust graph.
The payload is trivial. In a vulnerable image-fetch endpoint:
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
With IMDSv2 enforced, the same request fails because the attacker cannot obtain the session token via a reflected URL. The attack surface collapses at that single control.
What to change today
- Run the two CLI blocks above against every production region.
- Add the SCP to your organization root.
- Search Terraform for any module that sets
http_tokens = "optional"and remove the line. - Verify from the CloudTrail query
metadata-options.http-tokens = optional. The result set should be zero.
IMDSv2 enforcement is the single highest-leverage AWS hardening control and it can be completed before lunch. There is no workload-compatibility reason to delay.