aws-ec2-modify-userdata

derived aws emits CanModifyConfiguration

match (effective permission)

{ "action": "ec2:ModifyInstanceAttribute", "resource_type": "AWS::EC2::Instance" }

where

attribute == userData

emit

source typeIdentity
target type*
source<principal>
target<EC2 instance>
permissionsec2:ModifyInstanceAttribute ec2:StopInstances? ec2:StartInstances?
conditionsservice_state
state logicACTIVE if attacker also has ec2:StopInstances + ec2:StartInstances (can force boot) else CONDITIONAL(service_state - needs a reboot to trigger)

Narrative

{principal.name} can rewrite the UserData of {instance.name} (ec2:ModifyInstanceAttribute) so attacker code runs as root at next boot.

Raw rule rules/derived/aws/ec2.yaml

id: aws-ec2-modify-userdata
emits: CanModifyConfiguration
match_effective_permission:
  action: ec2:ModifyInstanceAttribute
  resource_type: AWS::EC2::Instance
optional_permission: ec2:StartInstances
where:
- attribute == userData
emit:
  source_type: Identity
  target_type: '*'
  source: <principal>
  target: <EC2 instance>
  permissions:
  - ec2:ModifyInstanceAttribute
  - ec2:StopInstances?
  - ec2:StartInstances?
  conditions:
  - service_state
  escalation: Rewrite UserData with a payload that runs as root on next boot; chains to CanExecuteAs the
    instance-profile role.
  state_logic: ACTIVE if attacker also has ec2:StopInstances + ec2:StartInstances (can force boot) else
    CONDITIONAL(service_state - needs a reboot to trigger)
  false_positive_note: UserData can only be modified while the instance is STOPPED, and by default runs
    only once per instance lifecycle. Without Stop/Start (or a natural reboot) the payload does not execute
    -> CONDITIONAL, not immediate RCE.
  narrative: '{principal.name} can rewrite the UserData of {instance.name} (ec2:ModifyInstanceAttribute)
    so attacker code runs as root at next boot.'
move · open · esc close