In 2018, researchers at Rhino Security Labs published a list of 21 distinct ways an AWS identity with seemingly modest permissions could escalate to full account control — no exploit code, no zero-day, just combinations of IAM actions that AWS itself considers legitimate. Methods like iam:CreatePolicyVersion, iam:PassRole chained with ec2:RunInstances, and iam:SetDefaultPolicyVersion remain the canonical reference for this class of risk, and open-source tools including Cloudsplaining, Prowler, and PMapper still implement checks derived directly from that original taxonomy. What makes IAM privilege escalation distinct from a typical vulnerability is that there is no patch to apply — the actions are documented AWS features, and the misconfiguration is a policy statement, not a software bug. A single overly broad iam:PassRole grant, or a trust policy missing an aws:SourceArn condition, can sit undetected in a production account for years because it never triggers a CVE feed and rarely shows up in a code review. This post walks through how these escalation paths work, why they persist, and what automated policy analysis actually needs to catch to close them.
What is an IAM privilege-escalation path, and why doesn't a CVE cover it?
An IAM privilege-escalation path is a sequence of permissions that, combined, let a principal grant itself more access than it was intended to have — using only actions AWS designed to work exactly that way. There's no CVE because nothing is broken: iam:CreatePolicyVersion is meant to let administrators version policies, and iam:PassRole is meant to let services assume roles on a user's behalf. The risk only appears when those actions are granted without the conditions that scope them. Rhino Security Labs' original research organized these into categories — modifying an existing policy version, updating an assume-role trust policy, creating new access keys for a higher-privileged user, and passing an over-privileged role to a compute service like EC2 or Lambda you can also control. Because every step is an authorized API call logged as normal activity in CloudTrail, these paths are easy to miss in a manual review and require policy-graph analysis, not signature matching, to detect before they're exploited.
How does iam:PassRole become an escalation path?
iam:PassRole becomes an escalation path when it's granted without a condition scoping which role can be passed to which service, because the action alone doesn't grant new permissions — it only becomes dangerous paired with a service that will execute code under that role. The classic pattern Rhino Security Labs documented: a user with iam:PassRole plus ec2:RunInstances launches an EC2 instance, attaches an administrator-privileged instance profile they were never directly granted, and retrieves temporary credentials for that role from the instance metadata service. The same pattern applies with lambda:CreateFunction and lambda:InvokeFunction, or AWS CodeStar and Data Pipeline in later variants of the research. AWS's own documentation recommends the fix directly: scope iam:PassRole with an iam:PassedToService condition key so a principal can only pass roles to the specific service it's meant to use, and never pair a broad iam:PassRole grant with resource-creation actions across more than one compute service.
What role do wildcard actions and resources play in these paths?
Wildcards turn a narrowly-scoped job function into an escalation vector because Action: "iam:*" or Resource: "*" doesn't just grant the permission a developer needed — it grants every permission in that namespace, including the ones on Rhino's list. A policy written to let a CI/CD role "manage IAM for its own service accounts" but scoped with iam:* on Resource: "*" also grants iam:CreatePolicyVersion and iam:AttachUserPolicy against every identity in the account, including administrators. This is why Cloudsplaining, an open-source AWS IAM policy scanner, specifically flags policies containing privilege-escalation-enabling wildcard combinations and "credentials exposure" actions like iam:CreateAccessKey on paths broader than the principal's own resources. The fix is scoping both dimensions at once: enumerate the specific actions a role needs rather than a service-level wildcard, and constrain Resource to the exact ARNs it should touch, not the account-wide *.
What is confused-deputy risk in cross-account IAM trust policies?
Confused-deputy risk occurs when a role's trust policy allows another AWS account to assume it, but doesn't verify which specific resource in that account is doing the assuming — letting an attacker who compromises any tenant of a multi-tenant service impersonate a different, more privileged tenant. AWS's own guidance addresses this directly: cross-account trust policies should include an aws:SourceArn or ExternalId condition tying the trust to a specific calling resource, not just the account ID, because account IDs alone don't distinguish between two different customers of the same SaaS vendor assuming into your account. An iam:UpdateAssumeRolePolicy grant compounds this risk further — Rhino Security Labs listed it as its own escalation path, since a principal that can rewrite a role's trust policy can add itself as a trusted principal on a role it doesn't currently have permission to assume, without touching a single permission policy.
Which tools actually catch these paths before they're exploited?
AWS IAM Access Analyzer, which added unused-access findings in November 2023 alongside its original external-access analysis, is the primary managed service for this: it generates findings on external access grants, internal access between accounts in an organization, and unused roles, credentials, and permissions based on last-accessed data, and it validates policies against more than 100 checks before deployment. Open-source tooling fills gaps Access Analyzer doesn't cover for escalation-specific logic — Cloudsplaining and PMapper both build a permissions graph across a whole account and flag the exact Rhino-style escalation chains, while Parliament performs deeper static linting of individual policy documents for overly permissive statements. None of these tools work from a point-in-time export alone, though — policies attached at runtime via console changes or ad hoc AttachRolePolicy calls are what actually drift from an account's Infrastructure-as-Code baseline, so continuous re-analysis matters more than a one-time audit.
How should teams design least-privilege IAM policies from the start?
Least-privilege IAM design starts from deny-by-default and adds only the specific actions and resources a workload has demonstrated it needs, using IAM Access Advisor's last-accessed data to right-size permissions that were originally over-scoped for convenience. Two additional control layers matter beyond individual identity policies: permission boundaries, which cap the maximum permissions any policy attached to a role can grant even if that role is later modified, and service control policies (SCPs) at the AWS Organizations level, which apply account-wide guardrails no individual IAM policy can override — for example, blocking iam:CreateAccessKey entirely for a workload account that should never issue long-lived keys. Condition keys like aws:PrincipalOrgID, aws:SourceIp, and aws:MultiFactorAuthPresent let teams keep necessarily broad grants (an admin role, for instance) usable only from expected networks or with MFA present, narrowing the practical blast radius of a compromised credential even when the policy document itself looks permissive on paper.