iam:PassRole Without a Service Condition Lets Any User Escalate to Admin
A broad iam:PassRole grant without a PassedToService condition lets low-privilege users attach administrator roles to resources they launch, silently escalating privilege with no denied-call signal.
Operational summary
At a glance
- Symptom
- A workload account grants a developer group iam:PassRole on a broad resource pattern such as arn:aws:iam::*:role/* alongside a narrow launch permission like ec2:RunInstances or…
- Likely cause
- iam:PassRole permits a principal to associate an IAM role with an AWS resource being created or configured.
- Impact
- Any principal holding this combination of permissions can obtain administrator-equivalent access to the account without any IAM policy naming them as an administrator, without triggering an access-denied event…
- Verification signal
- After narrowing the policy, validate in an isolated non-production account using IAM Policy Simulator or a scoped test principal: attempt to pass a role outside the newly permitted…
- Safe correction
- Add an explicit Resource constraint and, where the target service supports it, an iam:PassedToService condition to every PassRole statement, scoping it to the specific role ARNs the principal…
- Rollback or recovery
- If the narrowed policy blocks a legitimate workflow that was not identified during diagnosis, restore the previous policy document version using IAM's policy versioning (the prior version remains…
Symptom
A workload account grants a developer group iam:PassRole on a broad resource pattern such as arn:aws:iam::*:role/* alongside a narrow launch permission like ec2:RunInstances or lambda:CreateFunction. Weeks later, a security review or CloudTrail audit finds a low-privilege user has launched a resource with an administrator-level role attached, then used that resource’s temporary credentials to perform actions the user was never directly authorised to perform. No error occurred at any step; every API call was permitted by policy.
False Assumption
The team assumed that granting iam:PassRole alongside a narrow service-launch action (for example, only ec2:RunInstances, without wider EC2 permissions) was sufficiently restrictive because the user could not directly call iam:PassRole in isolation, and assumed PassRole only matters when combined with obviously dangerous permissions like iam:CreateRole or iam:AttachRolePolicy. In practice, PassRole is dangerous whenever it is combined with any action that attaches a role to a compute resource the user controls, because the resulting session inherits the role’s permissions regardless of the user’s own policy.
Root Cause
iam:PassRole permits a principal to associate an IAM role with an AWS resource being created or configured. AWS IAM evaluates PassRole independently of the target role’s own trust policy content once the trust policy allows the relevant service principal (for example ec2.amazonaws.com or lambda.amazonaws.com) to assume it. If the PassRole grant does not include an iam:PassedToService or a Resource constraint limiting which role ARNs can be passed, the grant applies to every role in the account that trusts that service, including any role carrying AdministratorAccess. The launch action then instantiates a resource whose execution identity is that administrator role, and the launching user can typically reach that resource’s temporary credentials through the service’s normal operational interface (for example, EC2 instance metadata or a Lambda execution context they can invoke or modify).
Impact
Any principal holding this combination of permissions can obtain administrator-equivalent access to the account without any IAM policy naming them as an administrator, without triggering an access-denied event, and without an obvious audit signal beyond a normal-looking RunInstances or CreateFunction call. The blast radius extends to every resource and service the passed role can reach, and detection is difficult because CloudTrail records a permitted API call rather than a denied one.
Diagnosis
Confirm the exposure using read-only analysis before changing anything. Use AWS IAM Access Analyzer’s policy check capability or manual policy review to identify principals with iam:PassRole where the Resource element is a wildcard or an overly broad pattern, and correlate against which roles in the account have permissive attached policies. Cross-reference CloudTrail PassRole and resource-creation events (RunInstances, CreateFunction, CreateAutoScalingGroup) to see which roles have actually been passed by which principals historically. Treat this as observation only: it establishes the scope of exposure but does not itself remediate anything.
Correction
Add an explicit Resource constraint and, where the target service supports it, an iam:PassedToService condition to every PassRole statement, scoping it to the specific role ARNs the principal legitimately needs to pass rather than a wildcard. Where roles differ significantly in privilege, separate the launch role from the administrative role entirely so no single PassRole grant can reach both. This is a policy edit, not an infrastructure change, and it is reversible by reapplying the prior policy document.
Validation
After narrowing the policy, validate in an isolated non-production account using IAM Policy Simulator or a scoped test principal: attempt to pass a role outside the newly permitted ARN set and confirm the call is denied with AccessDenied, then attempt to pass an explicitly permitted role and confirm the launch still succeeds for the legitimate use case. Re-run the Access Analyzer policy check to confirm no PassRole statement in the account still resolves to a wildcard resource without a compensating condition.
Rollback
If the narrowed policy blocks a legitimate workflow that was not identified during diagnosis, restore the previous policy document version using IAM’s policy versioning (the prior version remains available as a non-default version after the update) and re-open the change with the missing use case added explicitly to the new Resource list rather than reverting to a wildcard. Do not restore a wildcard PassRole grant as a permanent fix; treat any rollback as temporary and time-boxed pending a corrected scoped policy.
Prevention
Require every iam:PassRole statement in new or reviewed policies to carry either a specific Resource ARN list or an iam:PassedToService condition as a non-negotiable review gate before merge. Maintain a periodic (for example, monthly) automated scan using IAM Access Analyzer’s external access and unused access findings to catch drift back toward wildcard grants, and record the scan output as evidence that the control remains effective over time.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Add an explicit Resource constraint and, where the target service supports it, an iam:PassedToService condition to every PassRole statement, scoping it to the specific role ARNs the principal…
Validate the vendor-specific syntax in official documentation before applying it.
Verify, roll back or escalate
Verify
After narrowing the policy, validate in an isolated non-production account using IAM Policy Simulator or a scoped test principal: attempt to pass a role outside the newly permitted ARN set and confirm the call is denied with AccessDenied, then attempt…
Rollback
If the narrowed policy blocks a legitimate workflow that was not identified during diagnosis, restore the previous policy document version using IAM's policy versioning (the prior version remains available as a non-default version after the update) and re-open the change…
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.