← All posts Attack paths

AWS IAM privilege escalation: how attack paths form and how to break them

Key takeaways

  • IAM privilege escalation is an identity using the permissions it already has to reach permissions it was never meant to have.
  • The dangerous permissions are rarely the obvious admin ones. They are control-plane actions like iam:PassRole, iam:CreatePolicyVersion, and iam:UpdateAssumeRolePolicy.
  • No single permission looks alarming on its own. The escalation comes from the chain, which is why one-policy-at-a-time review misses it.
  • You break the path by cutting one edge, usually a PassRole grant, an over-scoped policy, or a loose trust policy.
  • Pathbreak works read-only, confirms which identities can reach admin, and hands you the single change that severs each path.

Almost every serious AWS incident unfolds the same way. An attacker lands on a low-privilege identity, a leaked access key, a compromised CI role, an app credential, and within a few steps they are an administrator. In most of these cases there is no zero-day. The attacker uses permissions the account handed out on purpose, in a combination nobody intended.

That combination is an IAM privilege escalation path. This post walks through how those paths form in AWS, the permission combinations that create them, why they hide from ordinary review, and how to find and break them.

What is IAM privilege escalation?

IAM privilege escalation is when an identity uses the access it already has to grant itself, or reach, more access than it was meant to have. In AWS this almost always happens on the control plane, the API surface that manages identities and permissions, rather than by breaking into a workload.

AWS does exactly what you told it to. If an identity can edit a policy, pass a role, or create a key, AWS lets it. Escalation usually comes from a permission that was granted on purpose, with a second-order effect nobody traced.

How privilege escalation paths form

Most AWS escalation paths fall into a handful of families. None of them require admin. Each takes a narrow write permission and turns it into broad power.

Passing a role to a service you control

The most common is iam:PassRole combined with a compute service. If an identity can pass a privileged role to an EC2 instance, a Lambda function, an ECS task, or a Glue job, and it can also launch that service, it can run code as the privileged role and inherit its permissions. The identity borrowed a role that had the permissions it lacked. This is why iam:PassRole is the permission to watch most closely, and why it should always be constrained by which roles can be passed and to which service.

Rewriting a policy you are allowed to edit

If an identity has iam:CreatePolicyVersion, it can publish a new version of a customer-managed policy with whatever permissions it likes, then set that version as the default. A policy that granted read access to one bucket becomes a policy that grants everything. The same idea applies to iam:PutUserPolicy, iam:PutRolePolicy, and iam:PutGroupPolicy for inline policies. The permission reads as routine policy maintenance, and it can rewrite whatever that policy grants.

Attaching a stronger policy

Permissions like iam:AttachUserPolicy, iam:AttachRolePolicy, and iam:AttachGroupPolicy let an identity attach an existing managed policy to itself or to a principal it controls. Attach AdministratorAccess and the climb is over in one call. This works because the permission was scoped too broadly.

Creating new credentials for a stronger identity

Several permissions let an identity create fresh credentials for a principal that already has more privilege. iam:CreateAccessKey on another user issues that user a new key. iam:CreateLoginProfile sets a console password for a user that has none. iam:UpdateLoginProfile resets one. In each case the attacker gains a stronger identity to authenticate as, instead of adding permissions to the one they already have.

Loosening a trust policy

A role's trust policy decides who can assume it. If an identity has iam:UpdateAssumeRolePolicy on a privileged role, it can rewrite that trust policy to allow itself, then call sts:AssumeRole and become the role. The related risk is a role that already trusts too broadly, a wildcard principal or a whole account, so that assuming it never required editing anything at all.

Why isolated permissions hide the path

Look at any one of those permissions in a policy review and it reads as reasonable. A deployment identity needs iam:PassRole. A platform team needs to manage policies. An automation user needs to rotate keys. Each grant has a good story.

The escalation only appears when you connect the grant to what it can reach. iam:PassRole is harmless until you ask which roles it can pass and whether the identity can also launch the service that would run them. A managed policy edit is fine until you notice the policy is attached to an admin role. The path only shows up when you look at the whole graph, and a review that takes one policy at a time never does. That is why a review can return hundreds of findings and still miss the chain that reaches admin.

Tools that map it

You do not have to reason about this by hand. AWS IAM Access Analyzer reports external and unused access and can validate policies. Open-source tools model the relationships: PMapper builds a graph of who can reach whom, Cloudsplaining flags policies with escalation-prone permissions, and offensive frameworks like Pacu enumerate known escalation methods. These are valuable, and they map candidates, the moves that could work.

Most tools stop short of proof. Knowing that an identity has iam:PassRole is not the same as knowing that, given every policy, boundary, and service control policy in force, that identity can reach an administrator today. Checking that is what turns a candidate into a validated path.

How defenders break these paths

The good news about chains is that you only have to cut one edge. A few durable moves close most escalation paths:

Constrain iam:PassRole so an identity can pass only specific roles, and add an iam:PassedToService condition so a role meant for Lambda cannot be passed to EC2. Treat iam: write actions, especially policy edits, attachments, and credential creation, as high-privilege and grant them narrowly. Put a permissions boundary on identities that must have IAM write permissions so they cannot exceed a ceiling. Use service control policies to cap effective privilege across the organization, so even a misconfigured account cannot reach admin. Tighten role trust policies to name exact principals, never a bare account or wildcard. Then monitor CloudTrail for the telltale calls: CreatePolicyVersion, AttachRolePolicy, UpdateAssumeRolePolicy, CreateAccessKey on an identity that does not normally make them.

The hard part is knowing which paths remain after all your controls interact, and keeping that answer current as policies change every day.

How Pathbreak approaches it

Pathbreak treats IAM the way an attacker does, as a graph of reachability rather than a list of policies. It reasons over your real identities, roles, policies, boundaries, and trust relationships, then resolves the authorization to confirm which identities can escalate to admin or reach sensitive data. It does this read-only. Nothing runs against your account, and nothing is changed.

When a path resolves, Pathbreak shows the full chain and hands you the one change that breaks it, the PassRole grant to remove, the policy to tighten, the trust relationship to close. You fix that edge instead of triaging a thousand unranked findings. You can see the mechanics on the how it works page, the chains we cover on the attack paths page, and how we handle access on the Trust and Security page.

Frequently asked questions

What is AWS IAM privilege escalation?

It is when an identity uses the permissions it already has to grant itself, or reach, more permissions than it was meant to have. In AWS this usually happens on the control plane, by passing a role, editing a policy, creating new credentials, or assuming a more privileged role. Pathbreak finds and validates these privilege escalation paths across AWS accounts.

Which IAM permissions most often enable privilege escalation?

Common enablers include iam:PassRole paired with a compute service such as EC2, Lambda, or Glue, iam:CreatePolicyVersion and iam:SetDefaultPolicyVersion, iam:AttachUserPolicy and iam:AttachRolePolicy, iam:CreateAccessKey on another user, iam:CreateLoginProfile, and iam:UpdateAssumeRolePolicy. Each lets an identity widen its own effective access. The Pathbreak AWS Privilege Escalation Map lists each of these permissions with the CloudTrail event and the change that breaks the path.

How do you prevent IAM privilege escalation in AWS?

Scope iam: write actions tightly, gate iam:PassRole with a PassedToService condition, use permission boundaries and service control policies to cap effective privilege, remove unused administrative permissions, and continuously validate which identities can reach admin rather than reviewing policies one at a time. Pathbreak ranks the fixes by how many validated paths each one breaks.

How does Pathbreak find IAM privilege escalation paths?

Pathbreak reasons over your real IAM policies, roles, and trust relationships, resolves the authorization, and confirms which chains reach admin or sensitive data. It works read-only, reports only the paths that resolve, and gives you the single change that breaks each one.

Related posts

Want to see which identities can reach admin in your AWS accounts?

Request a demo