aws-ec2-modify-userdata
match (effective permission)
{
"action": "ec2:ModifyInstanceAttribute",
"resource_type": "AWS::EC2::Instance"
}
where
attribute == userData
emit
| source type | Identity |
|---|---|
| target type | * |
| source | <principal> |
| target | <EC2 instance> |
| permissions | ec2:ModifyInstanceAttribute ec2:StopInstances? ec2:StartInstances? |
| conditions | service_state |
| state logic | ACTIVE 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.'