Cloud Security Wire
AWS Azure GCP RSS
AWS Misconfiguration critical

AWS IAM Privilege Escalation: PassRole and CreatePolicyVersion Attack Paths

A deep-dive into two of the most dangerous AWS IAM misconfigurations — iam:PassRole abuse and iam:CreatePolicyVersion — with exploitation chains, detection opportunities, and remediation guidance.

By Cloud Security Wire · ·
#iam#privilege-escalation#aws#least-privilege
Critical Severity

This issue has been assessed as critical severity. Review affected configurations immediately.

AWS IAM privilege escalation is one of the most reliable paths from a low-privilege foothold to full account compromise. Two permissions stand out as especially dangerous: iam:PassRole and iam:CreatePolicyVersion. Neither shows up in routine permission audits the way iam:* does, yet each alone can hand an attacker administrative access within minutes.

Understanding iam:PassRole

iam:PassRole allows a principal to attach an IAM role to an AWS service — for example, assigning a role to an EC2 instance, a Lambda function, or a Glue job. The permission itself sounds innocuous: “you can pass a role to a service.” The danger is that the service receiving that role then acts with the role’s permissions, not the caller’s.

The Escalation Chain

Consider a developer with the following policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iam:PassRole",
        "lambda:CreateFunction",
        "lambda:InvokeFunction"
      ],
      "Resource": "*"
    }
  ]
}

This looks like a developer policy — Lambda deployment access. But with iam:PassRole on *, the developer can:

  1. Create a Lambda function and assign it the AdministratorAccess-equivalent role
  2. Write a Lambda handler that calls sts:GetCallerIdentity and iam:CreateUser with admin privileges
  3. Invoke the function

The Lambda runs as the attached role, not as the developer. The developer has never been granted iam:CreateUser directly, but the end result is full privilege escalation.

High-Risk Service Pairings

iam:PassRole becomes critical when combined with any of these:

Service PermissionEscalation Method
lambda:CreateFunction + lambda:InvokeFunctionRun arbitrary code as a privileged role
ec2:RunInstancesLaunch an EC2 with an instance profile; access via SSM or metadata
glue:CreateJob + glue:StartJobRunExecute a Glue Python job as a privileged role
ecs:RunTaskRun an ECS task with a task execution role
sagemaker:CreateTrainingJobExecute a SageMaker training job with an attached role
codebuild:StartBuildExecute a CodeBuild project with a privileged service role

The common thread: any service that executes code and accepts a role via iam:PassRole is a potential escalation vector.

Understanding iam:CreatePolicyVersion

iam:CreatePolicyVersion allows a principal to create a new version of an existing managed IAM policy. AWS stores up to five versions per policy and lets you mark any version as the default. The permission iam:SetDefaultPolicyVersion promotes a version to active.

The Escalation Chain

Suppose a principal has a restrictive policy attached, but also holds iam:CreatePolicyVersion. The attack is straightforward:

# 1. Get the ARN of the policy attached to your user/role
aws iam list-attached-user-policies --user-name target-user

# 2. Create a new version that grants AdministratorAccess
aws iam create-policy-version \
  --policy-arn arn:aws:iam::123456789012:policy/DevPolicy \
  --policy-document '{
    "Version":"2012-10-17",
    "Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]
  }' \
  --set-as-default

The --set-as-default flag makes the new version active immediately. The principal now has full * access.

If iam:SetDefaultPolicyVersion is not available separately, create-policy-version --set-as-default achieves both in one call.

The Variant: iam:CreatePolicyVersion on Another User’s Policy

The attack is even more impactful when the policy targeted belongs to an admin user or role. If a service account holds iam:CreatePolicyVersion with a wildcard resource, it can overwrite any managed policy in the account — including policies attached to admin roles used by humans or CI/CD pipelines.

Automated Discovery with Enumerate IAM

Enumerate IAM and PMapper are the standard tools for mapping escalation paths:

# PMapper: generate a full privilege escalation graph
pip install principalmapper
pmapper graph create --account 123456789012
pmapper analysis --output-type text
pmapper query "who can escalate privileges?"

PMapper models 20+ escalation paths, including all the PassRole pairings above, and outputs a graph showing which identities can reach admin.

Detection

CloudTrail events to monitor:

  • iam:CreatePolicyVersion — especially when setAsDefault=true
  • iam:SetDefaultPolicyVersion on admin-scoped policies
  • lambda:CreateFunction or ec2:RunInstances where the passed role has admin-level access
  • iam:PassRole where the role ARN contains known privileged roles

Sample EventBridge rule (CloudWatch Logs Insights):

filter eventSource = "iam.amazonaws.com"
  and eventName in ["CreatePolicyVersion", "SetDefaultPolicyVersion"]
  and requestParameters.setAsDefault = "true"
| stats count(*) by userIdentity.arn, requestParameters.policyArn

Remediation

1. Constrain PassRole to specific roles:

{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::123456789012:role/lambda-execution-*",
  "Condition": {
    "StringEquals": {
      "iam:PassedToService": "lambda.amazonaws.com"
    }
  }
}

The iam:PassedToService condition key restricts which service the role can be passed to — this significantly reduces the blast radius.

2. Restrict CreatePolicyVersion to specific policy ARNs, never wildcard.

3. Enable IAM Access Analyzer to surface policies with broad PassRole grants and flag external access.

4. Run PMapper quarterly as part of your IAM audit process. Many organizations discover escalation paths that were introduced months prior via incremental permission additions.

5. Enforce SCPs at the Organization level:

{
  "Effect": "Deny",
  "Action": [
    "iam:CreatePolicyVersion",
    "iam:SetDefaultPolicyVersion"
  ],
  "Resource": "*",
  "Condition": {
    "ArnNotLike": {
      "aws:PrincipalArn": "arn:aws:iam::*:role/IAMAdminRole"
    }
  }
}

This SCP denies policy version management to everyone except a designated IAM admin role, regardless of what identity-level policies say.

Key Takeaways

iam:PassRole and iam:CreatePolicyVersion are force-multipliers. A principal with either permission and the right complementary permissions can reach admin without ever touching iam:*. Regular privilege escalation audits using tools like PMapper, combined with targeted CloudTrail alerting and restrictive SCPs, are the effective controls. Treating iam:PassRole as equivalent to iam:* in your risk model is not an overstatement.

← All Analysis Subscribe via RSS