Back to Blog
June 18, 2026

iam:PassRole Explained: The Most Abused IAM Permission

Of all the IAM permissions that appear in misconfigured AWS accounts, iam:PassRole is the one security teams most consistently underestimate. It does not give a principal admin rights on its own. It does something more subtle: it lets a principal hand a role to an AWS service. And that distinction is exactly what makes it so easy to abuse.

This article explains what iam:PassRole actually does at the API level, walks through the attack chains it enables, and shows you how to write a scoped, condition-locked version that does its job without opening a privilege escalation path.

What iam:PassRole Actually Does

When you launch an EC2 instance with an instance profile, create a Lambda function with an execution role, or start an ECS task with a task role, you are passing an IAM role to an AWS service. The service then assumes that role and uses its permissions to make API calls on your behalf.

AWS requires the calling identity to hold iam:PassRole on the target role before it can hand that role to a service. This is a guard against a user silently delegating permissions they do not themselves possess. The intent is sound. The problem arises when the permission is granted on Resource: "*" — meaning the holder can pass any role in the account to any service, including roles with administrator-level permissions.

In that configuration, iam:PassRole is not a narrow delegation control. It is a skeleton key.

Why It Leads to Privilege Escalation

The escalation is indirect and that indirectness is what hides it. An attacker (or overprivileged developer) does not gain admin permissions themselves. Instead they create a compute resource — a Lambda function, an EC2 instance, an ECS task — and attach an admin-level role to it. Then they invoke that resource and use its credentials to do whatever they want.

From CloudTrail's perspective the action that produced the admin credentials was alambda:InvokeFunction or an ec2:RunInstances, not an IAM write. Without looking at the assumed-role chain, the audit log reads as normal service traffic. This is why iam:PassRole on Resource: "*" shows up repeatedly in post-incident reports as the permission that made the breach possible.

The Dangerous Combinations

iam:PassRole is almost never exploited alone. Its damage comes from pairing it with a service-launch permission. These three combinations appear most often in the wild.

  • PassRole + RunInstances. iam:PassRole with ec2:RunInstances lets an attacker launch an instance with an administrator instance profile and immediately harvest instance metadata credentials via the IMDS endpoint. No code deployment is required — the credentials are available the moment the instance starts.
  • PassRole + Lambda. iam:PassRole with lambda:CreateFunction and lambda:InvokeFunctionlets an attacker deploy a function attached to a privileged execution role and run arbitrary AWS SDK calls through it. The function can exfiltrate data, create new users, or escalate further — all under the execution role's identity.
  • PassRole + ECS or Glue. The same pattern extends to ECS task definitions (ecs:RegisterTaskDefinition + ecs:RunTask) and AWS Glue jobs (glue:CreateJob + glue:StartJobRun). Both services accept an execution role at creation time. Both can run attacker-controlled code under that role's permissions.

How to Scope iam:PassRole Safely

The correct pattern locks iam:PassRole along two axes: the specific role ARNs that can be passed, and the service that is allowed to receive them. AWS provides the iam:PassedToService condition key for exactly this purpose.

Here is the unsafe version — the form most commonly found in the wild. It allows the holder to pass any role to any service:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "UnsafePassRole",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "*"
    }
  ]
}

Here is the safe, scoped version. It restricts the permission to a single named role and limits the receiving service to Lambda only. An attacker who holds this policy cannot pass a privileged role, and cannot pass any role to EC2, ECS, or Glue:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ScopedPassRole",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::123456789012:role/my-lambda-execution-role",
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "lambda.amazonaws.com"
        }
      }
    }
  ]
}

A few rules of thumb for writing safe iam:PassRole grants:

  • Never use Resource: "*". Always name the exact role ARN or use a path-based wildcard that covers only the least-privileged roles the workload needs.
  • Always add iam:PassedToService. Match it to the one AWS service that legitimately needs the role. If your workflow only needs Lambda, do not allow passing to EC2 as well.
  • Keep passed roles least-privileged. A scoped iam:PassRole on an over-permissioned execution role is still a risk. Both sides of the grant need to be tight.
  • Separate duty from deployment. Developers who deploy Lambda functions should not be able to create or modify the execution roles those functions use. Separate those actions across different principals.

Detecting Unsafe PassRole in Your Policies

The hardest part of auditing iam:PassRole is that the dangerous grant is not always obvious. It can appear as an explicit iam:PassRole on Resource: "*", but it also appears inside wildcards like iam:Pass* or iam:* — both of which include it implicitly. A scanner that only looks for the literal string iam:PassRole will miss those.

Things to look for when auditing manually:

  • Any statement where Action includes iam:PassRole, iam:Pass*, or iam:*, and Resource is "*" or a broad wildcard.
  • Any statement granting iam:PassRole without a iam:PassedToService condition, regardless of resource scoping.
  • The combination of iam:PassRole and a service-launch action (ec2:RunInstances, lambda:CreateFunction, ecs:RunTask, glue:CreateJob) anywhere in the same policy or policy set attached to the principal.
  • CloudTrail events for iam:PassRole that reference roles with AdministratorAccess or PowerUserAccess attached.

Rule-based scanners catch the obvious cases but miss the interaction-level risk. The question is not just "does this policy grant PassRole on *" — it is "given everything this principal can do, can they use PassRole to reach a privileged identity." That requires reasoning across the full permission set, not line-by-line matching.

Catch unsafe iam:PassRole before it ships

Paste an IAM policy and get AI-Powered analysis in seconds — free, no credit card.

Amazon Web Services (AWS) is a trademark of Amazon.com, Inc. Shieldly is not affiliated with, endorsed by, or sponsored by Amazon Web Services.