aws-asg-launch-template-passrole-bypass
Modify the $Latest/$Default launch-template version an ASG uses; the ASG service-linked role launches with the chosen instance profile even without caller iam:PassRole.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
?launch_template is used by some ASG at version $Latest or $Default
?principal has ec2:CreateLaunchTemplateVersion OR ec2:ModifyLaunchTemplate on ?launch_template
?principal sets IamInstanceProfile in the new/default version to a privileged role ?role
the ASG re-reads $Latest/$Default at next launch (health replacement, scaling, refresh)
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'principal': None}
CanTrigger {'asg': None}
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | <principal> |
| target | <instance-profile role set in the template version> |
| permissions | ec2:CreateLaunchTemplateVersion ec2:ModifyLaunchTemplate |
| conditions | service_state trigger_exists condition_expression |
| state logic | if matched(optional[0]) or a scaling policy/scheduled action exists: ACTIVE else CONDITIONAL(trigger_exists). If autoscaling:LaunchTemplateVersionSpecified is enforced (requires a numbered version): BLOCKED/POTENTIAL (condition_expression). |
Narrative
{principal.name} can modify the $Latest/$Default version of launch template {launch_template.name}; ASG {asg.name}'s service-linked role launches instances as {role.name}, executing code as {role.name} without iam:PassRole.
Raw rule rules/derived/aws/autoscaling.yaml
id: aws-asg-launch-template-passrole-bypass
emits: CanExecuteAs
description: Modify the $Latest/$Default launch-template version an ASG uses; the ASG service-linked role
launches with the chosen instance profile even without caller iam:PassRole.
match:
- - principal: null
- CanModifyConfiguration
- launch_template: null
where:
- ?launch_template is used by some ASG at version $Latest or $Default
- ?principal has ec2:CreateLaunchTemplateVersion OR ec2:ModifyLaunchTemplate on ?launch_template
- ?principal sets IamInstanceProfile in the new/default version to a privileged role ?role
- the ASG re-reads $Latest/$Default at next launch (health replacement, scaling, refresh)
optional:
- - principal: null
- CanTrigger
- asg: null
emit:
source_type: Identity
target_type: Identity
source: <principal>
target: <instance-profile role set in the template version>
permissions:
- ec2:CreateLaunchTemplateVersion
- ec2:ModifyLaunchTemplate
conditions:
- service_state
- trigger_exists
- condition_expression
state_logic: 'if matched(optional[0]) or a scaling policy/scheduled action exists: ACTIVE else CONDITIONAL(trigger_exists).
If autoscaling:LaunchTemplateVersionSpecified is enforced (requires a numbered version): BLOCKED/POTENTIAL
(condition_expression).'
escalation: Because launches from $Latest/$Default are executed by AWSServiceRoleForAutoScaling, the
caller passes a role to instances WITHOUT holding iam:PassRole.
false_positive_note: Applies ONLY when the ASG references $Latest/$Default (not a pinned numbered version)
AND autoscaling:LaunchTemplateVersionSpecified is not enforced to require a numbered version. The
create/update/refresh APIs run a RunInstances dry-run that checks the CALLER's PassRole, so the bypass
is the 'ASG later re-reads the alias' case, not the caller's own create call. If the version is pinned
or the condition key is enforced, emit BLOCKED/POTENTIAL.
narrative: '{principal.name} can modify the $Latest/$Default version of launch template {launch_template.name};
ASG {asg.name}''s service-linked role launches instances as {role.name}, executing code as {role.name}
without iam:PassRole.'