← marwandiallo.comlabs

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.

How this works. The engine knows 14 published techniques across AWS, Azure, and GCP. For each principal it evaluates every technique's permission set against the principal's effective permissions (direct + group-inherited), derives outbound edges, then BFS's from the starting principal looking for paths to the admin sentinel. Path enumeration capped at depth 6 / 25 paths to keep the UI responsive on adversarial inputs.

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)

path #12 hops
  1. start: user/dev-alice
  2. 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.
  3. 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.

fromtotechniquedetail
user/dev-alicerole/EC2-Adminaws-passrole-runinstancesRun an EC2 instance with role role/EC2-Admin attached, then read IMDS.
user/dev-alicerole/Read-Onlyaws-passrole-runinstancesRun an EC2 instance with role role/Read-Only attached, then read IMDS.
role/EC2-Adminadminimplicit-adminrole/EC2-Admin already holds an admin-equivalent permission.

Principal directory

user/dev-aliceuser
iam:PassRoleec2:RunInstancesssm:StartSessionec2:DescribeInstances
role/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-Onlyrole
s3:Get*s3:List*

Technique catalog (14)

critical[aws] aws-passrole-runinstancesiam:PassRole + ec2:RunInstances → instance with admin role

Launch 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.

rhinosecuritylabs.com

critical[aws] aws-passrole-lambdaiam:PassRole + lambda:CreateFunction + lambda:InvokeFunction

Create 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.

rhinosecuritylabs.com

critical[aws] aws-create-access-keyiam:CreateAccessKey on another user

Mint 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.

rhinosecuritylabs.com

critical[aws] aws-update-assume-role-policyiam:UpdateAssumeRolePolicy → assume any role

Rewrite 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.

rhinosecuritylabs.com

critical[aws] aws-attach-user-policyiam:AttachUserPolicy → AdministratorAccess on self

Attach 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.

rhinosecuritylabs.com

high[aws] aws-add-user-to-groupiam:AddUserToGroup → join admin group

Add 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.

rhinosecuritylabs.com

high[aws] aws-passrole-codebuildiam:PassRole + codebuild:UpdateProject

Modify 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.

hackingthe.cloud

high[aws] aws-cloudformation-passroleiam:PassRole + cloudformation:CreateStack

Create 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.

hackingthe.cloud

critical[azure] az-app-admin-roleApplication Administrator role → add credential to any non-protected SP

The 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.

posts.specterops.io

high[azure] az-app-ownerApplication Owner → add credential to that app

Owners 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.

posts.specterops.io

critical[azure] az-sp-graph-app-readwriteCompromised SP with Application.ReadWrite.All (app permission) → seize any SP

Application.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.

posts.specterops.io

critical[azure] az-managed-identity-vmVM Run Command → steal Managed Identity token

Use 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.

posts.specterops.io

critical[gcp] gcp-act-asiam.serviceAccounts.actAs + compute.instances.create

Launch 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.

rhinosecuritylabs.com

critical[gcp] gcp-create-keyiam.serviceAccountKeys.create

Mint 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.

rhinosecuritylabs.com