IAM Privilege Escalation
A live cloud-IAM attack-path enumerator. Each scenario seeds a small directory of users, roles, groups, and service principals; the engine derives every attack edge from the permissions held, then enumerates every path from the starting principal to admin. Edit the principal list and watch paths appear and disappear.
Scenarios
Setup. A developer user is granted iam:PassRole + ec2:RunInstances + ssm:StartSession 'for debugging'. There's an unrelated EC2-Admin role. The developer can launch an instance carrying that role and SSM in.
Starting principal: user/dev-alice
reference: Rhino Security Labs, AWS Privilege Escalation Methods (2018) — Method 8
Principals (editable)
Add, remove, or modify principals. Try removing iam:PassRole from the starting user to break the chain; try adding iam:AttachUserPolicy to enable a one-step path. Changes are reflected immediately in the path enumerator below.
Attack paths to admin (1)
- start:
user/dev-alice - iam:PassRole + ec2:RunInstances → instance with admin role (aws-passrole-runinstances)→
role/EC2-Admin: Run an EC2 instance with role role/EC2-Admin attached, then read IMDS. - implicit-admin (implicit-admin)→
admin: role/EC2-Admin already holds an admin-equivalent permission.
Derived attack edges (3)
Every transition the engine derived, even ones not on a path to admin. Useful for spotting near-misses and lateral movement.
| from | to | technique | detail |
|---|---|---|---|
user/dev-alice | role/EC2-Admin | aws-passrole-runinstances | Run an EC2 instance with role role/EC2-Admin attached, then read IMDS. |
user/dev-alice | role/Read-Only | aws-passrole-runinstances | Run an EC2 instance with role role/Read-Only attached, then read IMDS. |
role/EC2-Admin | admin | implicit-admin | role/EC2-Admin already holds an admin-equivalent permission. |
Principal directory
user/dev-aliceuseriam:PassRoleec2:RunInstancesssm:StartSessionec2:DescribeInstancesrole/EC2-Adminrole*Powerful role intended for admin EC2 instances; trust policy is the EC2 service principal so anyone who can pass it to an instance becomes admin.
role/Read-Onlyroles3:Get*s3:List*Technique catalog (14)
[aws] aws-passrole-runinstances — iam:PassRole + ec2:RunInstances → instance with admin roleLaunch an EC2 instance, pass an admin-equivalent role to it, then SSH/SSM in (or read instance metadata) to use that role.
requires: iam:PassRoleec2:RunInstancesssm:StartSession OR ec2:GetPasswordData
outcome: Full access to whatever IAM role you passed to the instance.
mitigation: Restrict iam:PassRole with a Condition on iam:PassedToService and a tight resource ARN allowlist. Disable IMDSv1, require IMDSv2 with hop-limit 1.
[aws] aws-passrole-lambda — iam:PassRole + lambda:CreateFunction + lambda:InvokeFunctionCreate a Lambda function with an admin role attached, invoke it, exfiltrate temporary credentials from the runtime.
requires: iam:PassRolelambda:CreateFunctionlambda:InvokeFunction OR lambda:CreateFunctionUrlConfig
outcome: Code execution under the admin role; can call sts:GetCallerIdentity, then any API the role allows.
mitigation: Same iam:PassRole condition. Treat lambda:CreateFunction as privileged. Lambda function URLs without auth should not be combinable with PassRole on a powerful role.
[aws] aws-create-access-key — iam:CreateAccessKey on another userMint a long-lived access key for a higher-privileged IAM user.
requires: iam:CreateAccessKey
outcome: Persistent credentials for the target user, even if the original credentials are rotated.
mitigation: iam:CreateAccessKey should be scoped to ${aws:username} via condition. Better: kill long-lived keys entirely and use IAM Identity Center / OIDC short-lived sessions.
[aws] aws-update-assume-role-policy — iam:UpdateAssumeRolePolicy → assume any roleRewrite the trust policy of an admin role to trust your identity, then sts:AssumeRole into it.
requires: iam:UpdateAssumeRolePolicysts:AssumeRole
outcome: Become any role whose trust policy you can edit.
mitigation: iam:UpdateAssumeRolePolicy should be a denied action even for admin (set in an SCP at the org level). Audit role-trust changes via CloudTrail and alert.
[aws] aws-attach-user-policy — iam:AttachUserPolicy → AdministratorAccess on selfAttach the AWS-managed AdministratorAccess policy to your own user.
requires: iam:AttachUserPolicy
outcome: Self-promotion to admin.
mitigation: Deny iam:AttachUserPolicy on the AdministratorAccess managed policy ARN at the SCP layer.
[aws] aws-add-user-to-group — iam:AddUserToGroup → join admin groupAdd yourself to a group whose policies grant admin (e.g. an 'Admins' group).
requires: iam:AddUserToGroup
outcome: Inherit every policy attached to the admin group.
mitigation: Group-membership changes alarmed via CloudTrail. Avoid group-as-permission-bag pattern; prefer policies attached to roles invoked via SSO.
[aws] aws-passrole-codebuild — iam:PassRole + codebuild:UpdateProjectModify a CodeBuild project's buildspec / service role to dump credentials in CloudWatch logs.
requires: iam:PassRolecodebuild:UpdateProjectcodebuild:StartBuild
outcome: Steal the CodeBuild project's role credentials at next build.
mitigation: Block changes to build infrastructure outside of approved change-management. Pin build roles to least privilege.
[aws] aws-cloudformation-passrole — iam:PassRole + cloudformation:CreateStackCreate a CloudFormation stack with an admin role; stack template provisions a backdoor.
requires: iam:PassRolecloudformation:CreateStack
outcome: Whatever the admin role can do, the stack can do — including new IAM users, roles, KMS grants.
mitigation: CloudFormation stack-level service role should be a deliberately-scoped change-management role, not a generic admin.
[azure] az-app-admin-role — Application Administrator role → add credential to any non-protected SPThe Application Administrator and Cloud Application Administrator AAD directory roles implicitly grant the ability to manage credentials on any application/SP not in the 'protected' category. Add a client secret to a privileged SP and sign in as it.
requires: AAD role: Application Administrator OR Cloud Application Administrator
outcome: Authenticate as the target SP, inheriting all its app role assignments — often Graph permissions equivalent to Global Admin.
mitigation: Treat Application Administrator as a Tier-0 role. Enable AAD 'protected actions' / Conditional Access on credential management. Audit appCredential changes via the Entra audit log; alert on additions to apps with high-privilege Graph grants.
[azure] az-app-owner — Application Owner → add credential to that appOwners of an application object can manage credentials on that specific app. If the app holds privileged Graph permissions, owning it is a privesc.
requires: Owner of the target Application object
outcome: Mint a client secret for the owned app's SP and sign in as it.
mitigation: Treat application ownership as a privileged grant. Periodically review owners on apps with privileged Graph consent. Avoid making low-privilege users owners of any app that has consented Application permissions.
[azure] az-sp-graph-app-readwrite — Compromised SP with Application.ReadWrite.All (app permission) → seize any SPApplication.ReadWrite.All as an *application* permission on a Service Principal lets that SP add credentials to any other app/SP. If the attacker has compromised this SP (leaked client secret, stolen managed-identity token), they can chain to any higher-privileged SP.
requires: Holding credentials for an SP granted Application.ReadWrite.All (app permission)
outcome: Mint credentials for any app/SP and sign in as it.
mitigation: Application.ReadWrite.All is the most-abused Graph permission. Avoid granting it; prefer Application.ReadWrite.OwnedBy. Audit consents quarterly. Rotate SP credentials and detect abnormal IPs on app-only auth.
[azure] az-managed-identity-vm — VM Run Command → steal Managed Identity tokenUse Microsoft.Compute/virtualMachines/runCommand/action on a VM with a powerful managed identity to read its IMDS token.
requires: Microsoft.Compute/virtualMachines/runCommand/action
outcome: AAD access token for the VM's managed identity, usable for any role assignment it has.
mitigation: Restrict runCommand. Strip Managed Identities from VMs that don't need them. Treat IMDS as authentication.
[gcp] gcp-act-as — iam.serviceAccounts.actAs + compute.instances.createLaunch a GCE instance running as a privileged service account, then read its metadata token.
requires: iam.serviceAccounts.actAscompute.instances.create
outcome: Bearer token for the service account's roles (often editor or owner on a project).
mitigation: iam.serviceAccounts.actAs should be conditioned with a resource constraint to specific SA names. Org policy: disable default SA on new projects.
[gcp] gcp-create-key — iam.serviceAccountKeys.createMint a JSON key for a privileged service account.
requires: iam.serviceAccountKeys.create
outcome: Persistent credentials for the target SA.
mitigation: Org policy: iam.disableServiceAccountKeyCreation. Workload Identity Federation everywhere.