Skip to content

IAM Privilege Escalation Detection

cloud-audit detects 64 IAM privilege escalation methods across 9 categories. This goes beyond static policy analysis -- it resolves actual escalation paths by combining IAM policies, trust relationships, and resource permissions.

The detection runs in two stages:

  1. Action-based detection (60 methods) -- matches each principal's effective permissions against a curated catalog of escalation patterns
  2. Lateral movement detection (4 methods) -- builds a directed graph of AssumeRole trust relationships and walks it from each non-admin principal toward admin roles

How It Works

  1. Calls GetAccountAuthorizationDetails to pull all IAM policies, roles, users, and groups
  2. Resolves effective permissions per principal (inline + attached + group policies)
  3. Walks escalation paths: can this principal reach admin through any sequence of allowed actions?
  4. Reports each path with the specific escalation method and affected principal

No additional permissions beyond iam:GetAccountAuthorizationDetails and iam:SimulatePrincipalPolicy are required. The check is read-only.

Categories

1. IAM Self-Mutation (13 methods)

The principal can modify its own (or an assumable role's) permissions to grant itself admin access.

  • iam:CreatePolicyVersion - create new version of an attached policy
  • iam:SetDefaultPolicyVersion - set a different policy version as default
  • iam:AttachUserPolicy - attach managed policy to self
  • iam:AttachRolePolicy - attach managed policy to a role
  • iam:AttachGroupPolicy - attach managed policy to own group
  • iam:PutUserPolicy - attach inline policy to self
  • iam:PutGroupPolicy - modify group the principal belongs to
  • iam:PutRolePolicy - attach inline policy to a role
  • iam:DeleteRolePolicy + sts:AssumeRole - remove blocking inline policy on assumable role
  • iam:DeleteUserPolicy - remove blocking inline policy on a user
  • iam:DetachRolePolicy + sts:AssumeRole - detach blocking managed policy from assumable role
  • iam:DetachUserPolicy - detach blocking managed policy from a user
  • iam:CreateServiceLinkedRole - create service-linked role with service-managed admin policies

2. Credential Access (8 methods)

The principal can obtain credentials for a more privileged identity, or bypass MFA defenses.

  • iam:CreateAccessKey - create access keys for another user
  • iam:CreateLoginProfile - set console password for another user
  • iam:UpdateLoginProfile - reset another user's password
  • iam:AddUserToGroup - add self to a more privileged group
  • iam:UpdateAccessKey - re-enable a disabled access key for another user
  • iam:DeactivateMFADevice - remove MFA from a user, bypass MFA-required policies
  • iam:DeleteVirtualMFADevice - delete a virtual MFA device, bypass MFA-required policies
  • ssm:GetParameter - read secrets stored in SSM Parameter Store (DB credentials, API keys)

3. PassRole + Service (23 methods)

The principal can pass a privileged role to an AWS service that executes code, or swap the role on an existing instance profile.

Original (legacy) entries:

  • PassRole + Lambda (lambda:CreateFunction + lambda:InvokeFunction)
  • PassRole + EC2 (ec2:RunInstances)
  • PassRole + ECS (ecs:RegisterTaskDefinition + ecs:RunTask)
  • PassRole + Glue (glue:CreateDevEndpoint)
  • PassRole + CloudFormation (cloudformation:CreateStack)
  • PassRole + DataPipeline (datapipeline:CreatePipeline)

Tier 1 additions (variants of services already covered):

  • PassRole + Glue Job (glue:CreateJob)
  • PassRole + Glue Job update (glue:UpdateJob)
  • PassRole + Glue Session (glue:CreateSession)
  • PassRole + ECS UpdateService (ecs:UpdateService + ecs:RegisterTaskDefinition)
  • PassRole + ECS RegisterTaskDefinition (auto-deploy via service refresh)
  • PassRole + CloudFormation UpdateStack (cloudformation:UpdateStack)
  • PassRole + EC2 instance profile attach (ec2:AssociateIamInstanceProfile)
  • PassRole + EC2 instance profile replace (ec2:ReplaceIamInstanceProfileAssociation)
  • PassRole + Lambda event source mapping (lambda:CreateEventSourceMapping)
  • Instance profile role swap (iam:RemoveRoleFromInstanceProfile + iam:AddRoleToInstanceProfile) - no PassRole required

Tier 2 additions (new compute primitives):

  • PassRole + CodeBuild (codebuild:CreateProject + codebuild:StartBuild) - buildspec runs with privileged role
  • PassRole + AppRunner (apprunner:CreateService) - container runs with privileged role
  • PassRole + SageMaker Notebook (sagemaker:CreateNotebookInstance) - Jupyter root with privileged execution role
  • PassRole + SageMaker Processing Job (sagemaker:CreateProcessingJob)
  • PassRole + SageMaker Training Job (sagemaker:CreateTrainingJob)
  • PassRole + Bedrock Agent (bedrock:CreateAgent) - agent invokes AWS APIs through tool use
  • PassRole + Step Functions (states:CreateStateMachine) - state machine invokes services with privileged role

4. Lambda Code Modification (2 methods)

The principal can modify existing Lambda function code to inject commands.

  • lambda:UpdateFunctionCode - replace function code directly
  • lambda:UpdateFunctionConfiguration + layers - inject via Lambda layer

5. Trust Policy Abuse (1 method)

The principal can modify role trust policies to allow itself to assume privileged roles.

  • iam:UpdateAssumeRolePolicy - rewrite the trust policy

6. Permission Boundary Bypass (4 methods)

The principal can remove or modify permission boundaries that restrict its effective permissions.

  • iam:DeleteRolePermissionsBoundary - remove boundary from an assumed role
  • iam:DeleteUserPermissionsBoundary - remove own boundary
  • iam:PutRolePermissionsBoundary - replace role boundary with a permissive one
  • iam:PutUserPermissionsBoundary - replace own boundary with a permissive one

7. Resource Policy Abuse (2 methods)

The principal can modify resource-based policies on Lambda functions or layers to invoke or hijack privileged code without modifying its source.

  • lambda:AddPermission - cross-account/cross-principal invocation of a privileged Lambda
  • lambda:AddLayerVersionPermission - share a malicious Lambda layer used by privileged functions

8. Compute Hijack (7 methods)

The principal can take over existing compute that already holds a privileged role, without needing iam:PassRole. The role is already attached - the attacker simply runs code in that environment.

  • ssm:SendCommand - execute arbitrary commands on a managed EC2 instance, code inherits the instance role
  • ssm:StartSession - interactive shell on a managed EC2 instance, shell inherits the instance role
  • ec2-instance-connect:SendSSHPublicKey - push an SSH key (60s validity) to an EC2 instance, login = shell with instance role
  • codebuild:UpdateProject + codebuild:StartBuild - hijack an existing build project's buildspec to run code with its existing role
  • apprunner:UpdateService - replace the running container image of an AppRunner service, retain the privileged role
  • glue:UpdateDevEndpoint - hijack an existing Glue dev endpoint to run code with its role
  • sagemaker:CreatePresignedNotebookInstanceUrl - generate a presigned URL for an existing notebook, gain access with its role

9. Lateral AssumeRole (4 methods)

The principal can become admin without ever modifying any policy - by assuming a role through trust relationships. cloud-audit builds a directed graph from every role's AssumeRolePolicyDocument and walks it from each non-admin principal toward admin roles.

  • AssumeRole:Direct - principal P has sts:AssumeRole and a role R trusts P explicitly; R has admin permissions (1 hop)
  • AssumeRole:Chain - multi-hop chain: P trusts A, A trusts B, B is admin (2-4 hops)
  • AssumeRole:WildcardTrust - role with Principal: "*" or Principal: { AWS: "*" } trust policy - any AWS identity can assume it
  • AssumeRole:CrossAccountRoot - role trusts an external account's root principal (arn:aws:iam::OTHER:root), giving any IAM identity in that account assume rights

Detection notes:

  • Same-account root expansion: a role trusting arn:aws:iam::SAME_ACCOUNT:root is treated as reachable by any principal in the same account with sts:AssumeRole
  • Bare account IDs (e.g., "123456789012") are normalized to arn:aws:iam::123456789012:root per AWS semantics
  • Conditions are flagged, not evaluated: if a trust policy contains a Condition block (MFA, ExternalId, SourceArn), the finding includes a warning to verify its effectiveness manually
  • Service-linked roles (path starting with /aws-service-role/) are excluded from the graph
  • Permission boundaries and SCPs are out of scope for v2.1.0

Attack Chain Integration

Escalation paths feed into three attack chains:

Chain Description
AC-34 PassRole Escalation to Admin
AC-35 Self-Escalation via IAM Policy Modification
AC-36 External Escalation via OIDC + Privilege Escalation

When an escalation path is detected, it is reported as check aws-iam-018 and also triggers the relevant attack chain if the prerequisite conditions are met.

CLI

cloud-audit scan                          # includes escalation detection
cloud-audit list-checks --id aws-iam-018  # show check details

Escalation findings include the principal ARN, the escalation method, and the target privilege level in the finding details.