IMDSv1 Leaves Role Credentials One GET Request Away
An EC2 instance still answering unauthenticated GET requests on 169.254.169.254 turns any server-side request forgery bug into instant credential theft. Application-layer WAFs and VPC segmentation never see the request, because it never leaves the instance, so the theft happens entirely inside your own trust boundary.
At a glance
- Unsafe setting
- EC2 instance metadata options left at HttpTokens=optional, permitting unauthenticated IMDSv1 requests.
- Failure trigger
- An application-layer SSRF vulnerability issues a GET request to 169.254.169.254 for the instance role's temporary credentials.
- Blast radius
- Attacker obtains live IAM role credentials from inside the trust boundary, bypassing WAF and network segmentation entirely.
- Recommended control
- Enforce HttpTokens=required via modify-instance-metadata-options, launch template defaults, and an SCP condition on ec2:RunInstances.
The Trap
IMDSv1 left reachable alongside IMDSv2 on the EC2 link-local metadata endpoint, 169.254.169.254.
The Default State
Instances launched through the console, older AMIs, or Terraform modules without an explicit metadata_options block default to HttpTokens = optional. This means the instance answers plain GET requests against /latest/meta-data/iam/security-credentials/<role-name> with no session token required, alongside the newer IMDSv2 flow that demands a PUT-issued token first. Most fleets running mixed AMI generations carry this setting silently forward through every AutoScaling launch template revision, because nobody re-audits metadata_options when patching the base image.
The Blast Radius
Any server-side request forgery bug in the application stack — an unvalidated URL fetch in an image resizer, an XML external entity parser, a PDF renderer, or an internal HTTP proxy — becomes a direct credential harvester. The attacker’s payload simply requests http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name> from inside the compromised process. No token, no authentication header, no interaction with any external network boundary. The request never crosses a security group, WAF, or NACL, because source and destination are the same host. The attacker walks away with temporary AWS credentials scoped to the instance role and pivots outward: S3 bucket enumeration, DynamoDB reads, further lateral IAM calls, exactly the mechanism behind the 2019 Capital One breach. Rotating the access keys afterwards does nothing, because STS temporary credentials expire on their own schedule and simply get re-harvested on the next request.
The Lead Mechanic Fix
Force IMDSv2 exclusively on every running instance:
aws ec2 modify-instance-metadata-options --instance-id i-0123456789abcdef0 --http-tokens required --http-put-response-hop-limit 1 --http-endpoint enabled
Bake the same values into every launch template’s MetadataOptions block so AutoScaling never reintroduces the default. Set the hop limit to 2 only where a containerised workload on ECS-on-EC2 needs the token to cross the bridge network to the task. Then close the gap organisation-wide with a Service Control Policy that denies ec2:RunInstances unless the request includes the condition key ec2:MetadataHttpTokens equal to required, and enable the AWS Config managed rule ec2-imds-access-check with automatic remediation so drift gets flagged within one evaluation cycle rather than at the next audit.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
aws ec2 modify-instance-metadata-options --instance-id i-0123456789abcdef0 --http-tokens required --http-put-response-hop-limit 1 --http-endpoint enabledMetadataOptionsec2:RunInstancesVerify, roll back or escalate
Verify
Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.
Rollback
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.