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.

derived aws emits CanExecuteAs

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanModifyConfiguration {'launch_template': None}

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 typeIdentity
target typeIdentity
source<principal>
target<instance-profile role set in the template version>
permissionsec2:CreateLaunchTemplateVersion ec2:ModifyLaunchTemplate
conditionsservice_state trigger_exists condition_expression
state logicif 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.'
move · open · esc close