Originally published at shieldly.io/blog.
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.
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. AWS requires the calling identity to hold iam:PassRole on the target role before it can hand that role to a service.
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*"
}
On Resource: *, the holder can pass any role in the account to any service — including roles with administrator-level permissions. iam:PassRole is not a narrow delegation control here. It is a skeleton key.
Why It Leads to Privilege Escalation
The escalation is indirect. An attacker with iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction:
- Creates a Lambda function and attaches an admin-level execution role
- Invokes the function with code that calls
iam:CreateUserorsts:AssumeRole - Gets admin-level AWS SDK access from inside the function
From CloudTrail's perspective, the action that produced the admin credentials was a lambda:InvokeFunction — not an IAM write. It's easy to miss in a post-incident review.
The Dangerous Combinations
PassRole + ec2:RunInstances — launch an instance with an administrator instance profile, harvest credentials from IMDSv1 immediately.
PassRole + lambda:CreateFunction — deploy a function with a privileged execution role, run arbitrary AWS SDK calls through it.
PassRole + ecs:RegisterTaskDefinition / glue:CreateJob — both accept execution roles and run attacker-controlled code.
How to Scope iam:PassRole Safely
Lock PassRole along two axes:
-
Specific role ARNs that can be passed (the
Resourceelement) -
The service allowed to receive them (the
iam:PassedToServicecondition key)
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/my-lambda-execution-role",
"Condition": {
"StringEquals": {
"iam:PassedToService": "lambda.amazonaws.com"
}
}
}
This allows passing exactly one role, only to Lambda. Any other role or any other service is denied.
Catch iam:PassRole risks automatically — paste a policy into Shieldly's free AI-Powered analysis. No signup, no credit card.
Launch offer: code 90Off2M — 90% off first 2 months. Pro from $1.90/mo. shieldly.io/pricing
Top comments (0)