LoFP LoFP / legitimate automation or administrators may change user data and restart instances during maintenance, image baking, or configuration fixes. review the caller identity, change tickets, and whether `user_agent.original` and `source.ip` match known tooling and networks (the rule groups on both together with `user.name`).

Techniques

Sample rules

AWS EC2 Stop, Start, and User Data Modification Correlation

Description

Identifies a short sequence of EC2 management APIs against the same instance that is consistent with modifying instance user data and forcing it to run on the next boot: ModifyInstanceAttribute with user data, followed by stop and start. Adversaries may update userData and cycle instance state so malicious scripts execute as root on Linux or as the system context on Windows. This rule correlates successful StopInstances, StartInstances, and ModifyInstanceAttribute events that reference userData within a five-minute window, grouped by instance, user.name, account, source IP, and user agent. A hit requires exactly three distinct API names in that bucket.

Detection logic

FROM logs-aws.cloudtrail-* 
| WHERE event.provider == "ec2.amazonaws.com" 
    and event.outcome == "success"
    and aws.cloudtrail.user_identity.type != "AWSService"
    and not (
      user_agent.original like "*Terraform*"
      or user_agent.original like "*Ansible*"
      or user_agent.original like "*Pulumi*"
    ) and not source.address in ("cloudformation.amazonaws.com", "servicecatalog.amazonaws.com")
    and
  (
   event.action in ("StopInstances", "StartInstances") or 
   (event.action == "ModifyInstanceAttribute" and aws.cloudtrail.request_parameters like "*userData=*")
   )
| grok aws.cloudtrail.request_parameters """instanceId=(?<Esql.instance_id>[^,}\]]+)"""
| STATS Esql.event_action_unique_count = COUNT_DISTINCT(event.action), 
        Esql.event_action_values = VALUES(event.action) by Esql.instance_id, user.name, cloud.account.id, Esql.time_bucket = DATE_TRUNC(5 minute, @timestamp) , user_agent.original, source.ip, source.as.organization.name, source.geo.country_name
| where Esql.event_action_unique_count == 3
| Keep Esql.*, user.name, cloud.account.id, user_agent.original, source.ip, source.as.organization.name, source.geo.country_name