Find the IAM Privesc: PassRole + Lambda Escalation
The challenge
This AWS IAM policy looks routine, but one permission lets the holder hand a powerful role to a Lambda they create and run - escalating from limited access to admin. Read the policy and type the exact permission that enables that escalation.
What you'll learn
- Recognise iam:PassRole on Resource * as a privilege escalation primitive
- Trace how CreateFunction + InvokeFunction + PassRole chain into admin access
- Tell tightly-scoped, benign statements apart from a dangerous one
- Explain why passing a role is more powerful than it looks
- Name the correct scoping: narrow Resource ARN plus a PassedToService condition
Skills tested
Prerequisites
- How AWS IAM policies (Statement, Action, Resource) are structured
- What an IAM role is and how Lambda assumes one
- Basic JSON reading
How it works
In AWS, a principal's power comes not only from what it can do directly but from what it can make other services do on its behalf. iam:PassRole is the permission to hand an existing IAM role to a service you are configuring. On its own it does nothing, but combined with a service that runs code under a passed role, it becomes one of the best-known cloud privilege-escalation primitives.
The DeployLambdas statement grants lambda:CreateFunction, lambda:InvokeFunction, and iam:PassRole on Resource: "*". That wildcard is the fatal part: it means the user can pass any role in the account. An attacker creates a Lambda, uses iam:PassRole to attach a high-privilege role (say one with AdministratorAccess) as the function's execution role, then invokes the function. The code inside runs with that role's permissions, so the attacker now executes arbitrary actions as an administrator - having started from a limited developer identity.
The other two statements are decoys done right: read-only CloudWatch and Logs metrics, and read/write to a single named S3 bucket. They are scoped and benign. The audit skill is noticing that the privesc is not any single Lambda action but the iam:PassRole that lets a created function inherit a powerful role. The answer is iam:PassRole.
Common mistakes
- Naming lambda:CreateFunction alone. Creating a function is harmless without the ability to attach a powerful role to it.
- Picking lambda:InvokeFunction. Running a function you already control is not escalation by itself.
- Flagging the read-only or S3 statements. They are scoped to specific resources and grant no escalation.
- Missing the Resource: * on PassRole. The wildcard is what lets the user pass any role; a narrow ARN would close the hole.
How to defend against it
Never grant iam:PassRole on Resource: "*". Scope exactly which roles can be passed and to which service, so a created function can only ever run as a least-privilege role.
- Restrict
iam:PassRoleto specific role ARNs the user legitimately needs. - Add a
Conditionwithiam:PassedToService(for examplelambda.amazonaws.com) so the role cannot be diverted elsewhere. - Separate the permission to create/invoke functions from the permission to pass roles wherever possible.
- Run access-analyzer / IAM policy linting in CI to flag
PassRolewith wildcard resources.