Join our Newsletter — 33% off our NHI Course
Home› Guides› How to Find IAM Privilege Escalation Paths in…
Guide Cloud & Workload Identity

How to Find IAM Privilege Escalation Paths in AWS: A repeatable hunt from export to fix

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 11 November 2026 12 min read
On this page

In AWS a principal does not need an administrator policy to become an administrator: it only needs one permission that lets it change its own access, create credentials for someone stronger, or hand a stronger role to a service it controls. These privilege escalation paths are rarely visible in a policy review, because each permission looks reasonable on its own. This guide is a defender's runbook for finding them across an account or an AWS Organization: export the IAM data, test every principal for the high-risk permission classes with the policy simulator and IAM Access Analyzer, follow role trust chains, then rank and fix what you find. It is for cloud security engineers and IAM reviewers. The policy patterns behind the fixes are covered in the AWS IAM Policies and Roles Guide and the Top 10 AWS IAM Mistakes; this page is about the hunt.

This is an independent guide from NHI Mgmt Group, based on AWS's public documentation as of November 2026. It is not sponsored or reviewed by AWS. Product features change, so confirm settings against AWS's current documentation.

Key takeaways

  • Hunt by permission class, not by policy name. Four classes cover most paths: IAM policy edits, credential creation for other principals, trust policy edits, and passing roles to compute.
  • Test effective access, not text. aws iam simulate-principal-policy evaluates identity policies, the attached permissions boundary and SCPs, so a permission blocked by a guardrail drops out of the results.
  • PassRole leaves no event of its own. AWS states that PassRole is a permission, not an API call, so the evidence sits in the CreateFunction, RunInstances or similar call that received the role.
  • Follow the chain. A modest role that can assume, or pass, a stronger role inherits that role's reach. Score a path by where it ends, not where it starts.
  • Block regressions in the pipeline. IAM Access Analyzer check-access-not-granted fails a policy that grants any action on your sensitive list before it is deployed.

The permission classes to look for

The table lists the families of permissions that let a principal raise its own privilege. It is a detection list, not a procedure: each row is something a reviewer should treat as administrative.

ClassExample actions to testWhy it escalatesWhere the evidence appears in CloudTrail
Policy editsiam:CreatePolicyVersion, iam:SetDefaultPolicyVersion, iam:AttachUserPolicy, iam:AttachRolePolicy, iam:PutUserPolicy, iam:PutRolePolicy, iam:AttachGroupPolicy, iam:PutGroupPolicy, iam:AddUserToGroupThe principal can widen its own or a stronger principal's permissionsThe named IAM calls themselves
Credentials for othersiam:CreateAccessKey, iam:CreateLoginProfile, iam:UpdateLoginProfileThe principal can sign in or call APIs as a more privileged userThe named IAM calls, with a target user different from the caller
Trust editsiam:UpdateAssumeRolePolicyThe principal can add itself to the trust policy of a stronger roleUpdateAssumeRolePolicy, then AssumeRole
Guardrail removaliam:DeleteRolePermissionsBoundary, iam:DeleteUserPermissionsBoundary, iam:PutRolePermissionsBoundaryRemoves the ceiling that made a delegated permission safeThe named IAM calls
Pass role to computeiam:PassRole combined with create or update rights on a compute service, for example lambda:CreateFunction, ec2:RunInstances, cloudformation:CreateStack, glue:CreateDevEndpointCode the principal controls runs with the passed role's permissionsThe create or update call, whose request parameters name the role ARN
Update existing computelambda:UpdateFunctionCode on a function with a privileged role; ssm:SendCommand on an instance with a privileged profileThe principal runs its own code under a role it was never givenThe update or command call
Assume rolests:AssumeRole on * or on privileged roles whose trust policy allows the callerDirect hop to a stronger roleAssumeRole

Extend the list for your estate. Any service that accepts a role ARN and runs customer code (container tasks, notebooks, build projects, data pipelines) belongs in the pass-role class.

Before you start

  • Access: a read-only audit role in each account with IAM read permissions, iam:SimulatePrincipalPolicy and access-analyzer:CheckAccessNotGranted. Simulation output shows other principals' effective permissions, so we recommend treating this role and its output as sensitive.
  • Scope: start with production accounts and the management account. Exclude service-linked roles, which are controlled by their owning service.
  • Define "privileged": agree which roles are targets, for example any role with AdministratorAccess, IAM write, or access to production data stores. A path matters because of where it ends.
  • Cost: AWS describes charges for IAM Access Analyzer custom policy checks; confirm current rates on the IAM Access Analyzer pricing page, and run checks on changed policies in the pipeline rather than on every policy every day.

Step-by-step

All steps are read only until step 7.

  1. Export the account's IAM data.
    aws iam get-account-authorization-details --output json > iam-auth-ACCOUNT_ID.json
    This returns users, groups, roles, their inline and attached policies with versions, and role trust policies in one file. Keep it as the baseline for the hunt and for later comparison.
  2. List candidate principals. From the export, take every user and role except service-linked roles. Mark which ones are non-human (roles assumed by services, CI, workloads); in most estates they far outnumber people and are reviewed less often.
  3. Simulate the high-risk actions for each principal.
    aws iam simulate-principal-policy \
      --policy-source-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME \
      --action-names iam:CreatePolicyVersion iam:AttachRolePolicy iam:PutRolePolicy \
        iam:CreateAccessKey iam:UpdateAssumeRolePolicy iam:PassRole \
        iam:DeleteRolePermissionsBoundary lambda:CreateFunction lambda:UpdateFunctionCode \
      --query "EvaluationResults[?EvalDecision=='allowed'].[EvalActionName,EvalResourceName]" \
      --output text
    The resource defaults to *, which answers "is this allowed anywhere". For a precise answer, rerun with --resource-arns set to your privileged roles and users: a PassRole scoped to role/app-* is only a path if a privileged role matches the prefix. The simulator applies the entity's permissions boundary and SCPs, but it does not fetch resource-based policies, does not support resource control policies, and AWS warns that results can differ from live behaviour for role chaining and some advanced configurations. Treat an allowed as a finding to confirm, and an implicitDeny as reasonably reliable for identity policies.
  4. Pair pass-role findings with compute rights. PassRole alone does nothing. A principal is on a pass-role path only if it can also create or update something that runs with the passed role, and the passed role is stronger than the caller. Record the pair (for example PassRole on role/data-* plus lambda:CreateFunction) and the strongest role it can reach.
  5. Follow trust chains. For each privileged role, read its trust policy from the export and list who can assume it: principals in the same account with sts:AssumeRole on it, other accounts, and federated identity providers. Then repeat for those principals. A role that trusts an account root lets any principal in that account with suitable STS permissions assume it. Stop at three hops for the first pass; most real paths are short.
  6. Rank the paths. Score each by end point, number of hops and how the start principal authenticates (long-term key, federated CI, human SSO). Use the priority table below.
  7. Fix, starting with the cheapest break in each chain. Usually that is scoping the first hop: restrict PassRole by role ARN with iam:PassedToService, remove IAM write actions from workload roles, replace root principals in trust policies with specific role ARNs, and require a permissions boundary with a deny on its removal for teams that create roles. AWS warns against controlling PassRole with resource tags because the results are not reliable. Make the change in infrastructure as code, deploy, and watch for AccessDenied from the changed principal.

Prioritise what you find

PathPriority (NHI Mgmt Group view)Typical fix
Principal with a long-term access key can reach an administrator role in one hop1Remove the IAM write or PassRole permission; move the workload to a role
CI or federated role can edit policies or trust on itself or others1Remove IAM write from CI; deploy IAM through a separate, reviewed pipeline role
Unscoped PassRole plus compute create rights2Scope PassRole to named role ARNs with iam:PassedToService
Role creation without a locked permissions boundary2Require the boundary on create and deny boundary deletion
Multi-hop chain ending in production data access3Break the weakest hop; document any chain you keep

Make it continuous

  • Pipeline gate: run aws accessanalyzer check-access-not-granted --policy-type IDENTITY_POLICY --policy-document file://policy.json --access '[{"actions":["iam:PassRole","iam:CreatePolicyVersion","iam:AttachRolePolicy","iam:PutRolePolicy","iam:UpdateAssumeRolePolicy","iam:CreateAccessKey"]}]' on every changed workload policy. A FAIL means the policy might allow at least one listed action; the check accepts up to 100 actions and 100 resources per access entry. Allow exceptions only for the IAM deployment role.
  • Diff the export: rerun step 1 weekly and compare with the baseline; new IAM write or PassRole grants go to the review queue.
  • Detect use: alert on CreatePolicyVersion, SetDefaultPolicyVersion, Attach*Policy, Put*Policy, UpdateAssumeRolePolicy, CreateAccessKey and CreateLoginProfile where the target differs from the caller, and on Delete*PermissionsBoundary. For PassRole, alert on CreateFunction, RunInstances and CreateStack events whose request parameters reference privileged role ARNs.
  • Trim unused rights: IAM Access Analyzer unused access findings and last accessed data often show that the risky permission was never used. Removing it is then low risk. See the IAM Access Analyzer Guide.

Verify it worked

  • Re-simulate: the same simulate-principal-policy call for a fixed principal returns implicitDeny or explicitDeny for the removed actions, or allowed only on non-privileged resource ARNs.
  • Re-walk the chain: the path's end point is no longer reachable from its start principal.
  • Pipeline: a test policy containing iam:PassRole on * fails the gate.
  • No breakage: CloudTrail shows no new AccessDenied errors from the workload over a full release cycle.

Rollback and common errors

SituationFix
Deployment fails after PassRole scopingRead the role ARN in the error, add it to the allowed list or prefix, and check iam:PassedToService names the right service principal
Customer managed policy edit breaks a workloadSet the previous policy version as default; AWS keeps up to five versions
Simulator says allowed, live call is deniedA resource policy, RCP, VPC endpoint policy or session policy is denying it; record the guardrail as a compensating control
Simulator says denied, but CloudTrail shows successThe access came through a resource-based policy or a role chain the simulator does not evaluate; follow the trust path manually
Team cannot create roles after boundary enforcementConfirm their create calls pass the boundary ARN; adjust the boundary, not the deny

Small account or large organisation

A single account with tens of roles can be covered with the export, a short loop over simulate-principal-policy, and a spreadsheet of trust relationships. An organisation with hundreds of accounts needs the audit role deployed by StackSets, results collected centrally, and a graph of trust relationships across accounts, because many paths cross account boundaries. Purpose-built graph tooling helps at that scale; whatever you use, validate its findings with the simulator before raising a change.

Metrics

MetricTarget direction
Principals able to reach an administrator role in one hop (excluding approved admin roles)Zero
Workload roles with unscoped iam:PassRoleZero
Non-IAM-pipeline roles with any IAM write actionFalling to zero
Delegated roles carrying the required permissions boundary100%
Changed policies checked by the pipeline gate100%
Median days from new path found to fixFalling

Common failure patterns

  • Grepping for "Action": "*": administrator policies are found, while iam:PutRolePolicy on a CI role passes review.
  • Reviewing permissions without targets: PassRole on * is reported, but nobody checks which privileged roles it can reach, so the fix is never prioritised.
  • Ignoring the second hop: each role is reviewed in isolation and a trust chain to production is missed.
  • Boundary without a lock: a permissions boundary is required at creation, but nothing denies deleting it afterwards.
  • Trusting the simulator as live truth: AWS states results can differ from live behaviour; confirm critical findings and fixes with CloudTrail.

Framework mapping

FrameworkReferenceHow this task supports it
NIST SP 800-53 Rev 5AC-6 Least Privilege; AC-6(5) Privileged Accounts; AC-6(9) Log Use of Privileged FunctionsRemoves hidden administrative capability; logs its use
NIST CSF 2.0PR.AA-05Permissions reviewed with least privilege and separation of duties
MITRE ATT&CKT1098.001 Additional Cloud Credentials; T1098.003 Additional Cloud Roles; T1548.005 Temporary Elevated Cloud Access; T1078.004 Cloud AccountsDetection and prevention of the escalation behaviours in the class table

Practitioner checklist

  • Deploy a read-only audit role and treat its output as sensitive.
  • Export IAM data with get-account-authorization-details for every in-scope account.
  • Define the privileged end points before hunting.
  • Simulate the high-risk action list for every user and role, then rerun against privileged resource ARNs.
  • Pair PassRole findings with compute create and update rights.
  • Walk trust policies for privileged roles to three hops.
  • Fix the cheapest break in each chain through infrastructure as code.
  • Gate changed policies with check-access-not-granted and alert on IAM write events.

Standards and references

Related NHI Mgmt Group resources: AWS IAM Policies and Roles Guide · Top 10 AWS IAM Mistakes · IAM Access Analyzer Guide · How to find unused AWS IAM roles and access keys · Cloud PAM and CIEM Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 11 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org