Privilege Escalation in AWS occurs when a principal obtains permissions beyond what was intended. This typically happens through IAM misconfigurations that allow a user to modify their own permissions or create new high-privilege identities.
iam:CreatePolicy + iam:AttachUserPolicy: create an admin policy and attach it to yourselfiam:PassRole + lambda:CreateFunction: pass a high-privilege role to a new Lambda function you controliam:CreateLoginProfile: create console access for another user, then log in as themiam:UpdateAssumeRolePolicy: modify a role's trust policy to allow yourself to assume itsts:AssumeRole on overly permissive roles: assume a role with broader permissionsiam:CreatePolicyVersion with --set-as-default: rewrite an existing attached policy instead of attaching a new oneiam:CreateAccessKey on another user: mint long-term credentials for a more privileged identityiam:*, scope IAM permissions carefullyiam:PassRole to specific role ARNsThe single most common enabler is an unscoped iam:PassRole. A team gives its CI/CD pipeline or its developers permission to deploy compute, so the policy includes lambda:CreateFunction, ec2:RunInstances, or ecs:RegisterTaskDefinition, plus iam:PassRole on Resource: "*" because nobody knew which role ARNs to list. That combination is a full escalation path. The holder creates a Lambda function, passes the account's most privileged existing role to it (a Terraform execution role or a break-glass admin role), and invokes the function with code that calls whatever API they want. The pipeline identity itself never had admin permissions, yet it can execute as admin.
The same pattern shows up with EC2: pass an admin instance profile to an instance you launch, then read credentials from the instance metadata service. And with Glue, SageMaker, CloudFormation, and Data Pipeline, each of which accepts a role and runs code under it. The fix is always the same: iam:PassRole must name the exact role ARNs the identity is allowed to pass, and ideally use the iam:PassedToService condition key so a role meant for Lambda cannot be passed to EC2.
One detail that catches people during investigations: PassRole is a permission, not an API call, so there is no CloudTrail event named PassRole. To see which roles were passed to which services, you have to review the log entry for the action that created or modified the resource, for example the CreateFunction event for a Lambda function.
A second frequent path is policy version manipulation. An identity that holds iam:CreatePolicyVersion on a policy already attached to it can publish a new default version containing "Action": "*". No attach action is needed, so policies that only deny iam:AttachUserPolicy miss it entirely.
The IAM policy simulator answers "could this identity do X" without actually calling the API:
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::123456789012:role/ci-deploy-role --action-names iam:PassRole iam:CreatePolicyVersion iam:AttachRolePolicy lambda:CreateFunction
Both --policy-source-arn (the user, group, or role to evaluate) and --action-names (one or more actions in service:Action form) are required. The output returns an EvalDecision per action with one of three values: allowed, explicitDeny, or implicitDeny. Anything that comes back allowed for a role-passing or IAM-writing action deserves a closer look. Run this against your CI/CD roles and developer roles as a regular check, not just once.
Does a permission boundary stop privilege escalation?
It caps what the escalated identity can end up with, which is genuinely useful, but only if the boundary itself is protected. If the identity can call iam:DeleteRolePermissionsBoundary or modify the boundary policy, the ceiling is removable. Deny those actions explicitly, and pair boundaries with an SCP so the guardrail survives account-level mistakes.
Will GuardDuty detect privilege escalation?
GuardDuty produces findings for suspicious IAM behavior such as anomalous API calls and credentials used from unusual locations, and it can flag activity consistent with escalation attempts. It is a detection layer, not a prevention layer, and it will not catch an escalation that looks like normal deployment activity. Prevention through scoped iam:PassRole and boundaries matters more.
Which permissions should I treat as effectively administrative?
Any permission that lets an identity create or modify identities and policies, or pass a role to a compute service. That includes iam:CreatePolicyVersion, iam:AttachUserPolicy, iam:AttachRolePolicy, iam:PutUserPolicy, iam:PutRolePolicy, iam:UpdateAssumeRolePolicy, iam:CreateAccessKey, iam:CreateLoginProfile, iam:UpdateLoginProfile, and unscoped iam:PassRole. Treat granting any of these with the same review you would give AdministratorAccess.
The security principle of granting only the minimum permissions needed to perform a task - no more, no less.
An advanced IAM feature that sets the maximum permissions an IAM entity can have, acting as a ceiling on what identity-based policies can grant.
The scope of impact when a security incident occurs - how many resources, accounts, or users are affected. Smaller blast radius means better security posture.
A JSON document that defines permissions - which actions are allowed or denied on which AWS resources, and under what conditions.
Toc Consulting: AWS Security & Cloud Architecture
Our team helps engineering teams secure and architect AWS the right way: assessment in week one, a prioritized action plan in week two.