Stealing Instance Role Credentials From the Metadata Service
The challenge
You have a shell on a cloud instance. The instance carries an attached role whose temporary credentials are exposed on the link-local metadata service. Query the metadata service for the role's token, use it to authenticate to the internal storage endpoint, and read the flag it guards.
What you'll learn
- Query the link-local metadata service for instance role data
- Walk the iam/security-credentials path to find the role name
- Extract the temporary Token from the role credential blob
- Replay the token against an internal endpoint to read protected data
- Explain how IMDSv2 and least-privilege roles break this chain
Skills tested
Prerequisites
- Comfortable with curl and reading JSON output
- Understand that a cloud instance can carry an attached IAM role
- Know that an API token is passed in a request header
How it works
Cloud instances often run with an attached role so the application can call cloud services without anyone hardcoding long-lived keys. The platform delivers that role's temporary credentials through a special link-local address, 169.254.169.254, reachable only from the instance itself. Code that needs a credential queries this metadata service at boot and on refresh. It is convenient and keyless, which is exactly why it is a prime target.
Any process on the instance - including a shell an attacker just landed in - can reach the same address. Walking the path is mechanical: iam/security-credentials/ lists the role name, here s3-backup-role, and requesting iam/security-credentials/s3-backup-role returns a JSON document containing an AccessKeyId, a SecretAccessKey, and a short-lived Token. Whoever reads that JSON now holds the same permissions the instance was trusted with.
The final step turns those credentials into loot. The instance's notes mention a backup to a secrets bucket via the internal storage endpoint at 10.0.0.40. Sending the stolen Token as the X-Instance-Token header to http://10.0.0.40/s3/acme-secrets/flag.txt authenticates the request and returns the flag. The chain - shell, metadata token, authenticated storage read - is one of the most common cloud escalation paths, and it works because a foothold on the host inherits the host's role.
Common mistakes
- Hitting the storage endpoint with no token.
10.0.0.40returns403 missing or invalid x-instance-tokenuntil you supply the metadata token as a header. - Guessing the role name. The role is not arbitrary - it is listed under
iam/security-credentials/ass3-backup-role; you must read it, not invent it. - Grabbing the wrong field. The endpoint wants the
Tokenvalue, not theAccessKeyIdorSecretAccessKey. - Trying the metadata IP from off the box.
169.254.169.254is link-local and only answers from the instance itself - this is why a shell on the host is what unlocks it.
How to defend against it
The metadata service is a feature, not a bug, but it must be hardened so a foothold does not automatically become cloud access:
- Enforce IMDSv2 (session-token required, hop limit set to 1) so a simple server-side request forgery or a basic curl cannot reach the metadata endpoint.
- Apply least privilege to the instance role - a backup box should not hold a role that can read every secret in the account.
- Scope and shorten credential lifetimes, and alert on role credentials being used from an IP that is not the instance.
- Require strong, per-identity authentication on internal endpoints like the storage service rather than accepting any token the instance presents.
If instance credentials are believed stolen, revoke the session, rotate the role's trust, and review what that role could access - temporary credentials are still dangerous until they expire.