LoFP LoFP / inventory jobs, configuration management, and security scanners that are not in the exclusion list may read `userdata` across a large fleet. confirm the principal arn, source ip, asn, and user agent against approved automation before treating the activity as malicious.

Techniques

Sample rules

AWS EC2 Bulk User Data Enumeration

Description

Identifies when the same AWS principal, from the same source IP, successfully calls EC2 “DescribeInstanceAttribute” for the “userData” attribute against more than 20 distinct instances within a 10-minute lookback. Instance user data often contains bootstrap scripts and hardcoded credentials, so retrieving it across many instances in a short window can indicate reconnaissance or credential harvesting. Known CSPM roles, AWS service invocations, and Terraform calls from major cloud and carrier networks are excluded.

Detection logic

from logs-aws.cloudtrail-*
| where KQL("""event.provider: "ec2.amazonaws.com" and event.action: "DescribeInstanceAttribute" and event.outcome: "success" and aws.cloudtrail.flattened.request_parameters.attribute: "userData" and source.ip: * and source.as.organization.name: * and not user.name: (*CrowdStrikeCSPMReader* or *OrcaSecurityRole* or *AikidoSecurityRole* or *streamsec-*) and not aws.cloudtrail.user_identity.invoked_by: ("AWS Internal" or "cloudformation.amazonaws.com" or "aidevops.amazonaws.com" or "elasticmapreduce.amazonaws.com" or "aiops.amazonaws.com")""")
  and not (
    user_agent.original like "*Terraform*"
    and source.as.organization.name in (
      "Amazon.com, Inc.",
      "Google LLC",
      "Microsoft Corporation",
      "Verizon Business",
      "AT&T Enterprises, LLC"
    )
  )
| grok aws.cloudtrail.request_parameters """instanceId=(?<Esql.instance_id>[^,}\]]+)"""
| where Esql.instance_id is not null
| stats
    Esql.count_distinct_instance_id = count_distinct(Esql.instance_id),
    Esql.aws_request_parameters_values = values(aws.cloudtrail.request_parameters),
    Esql.user_agent_values = values(user_agent.original)
  by
    data_stream.namespace,
    aws.cloudtrail.user_identity.arn,
    aws.cloudtrail.user_identity.type,
    source.as.organization.name,
    source.ip, 
	source.`as`.number
| where Esql.count_distinct_instance_id > 20
| keep
    Esql.count_distinct_instance_id,
    data_stream.namespace,
    aws.cloudtrail.user_identity.arn,
    aws.cloudtrail.user_identity.type,
    source.ip,
	source.`as`.number,
    source.as.organization.name,
    Esql.aws_request_parameters_values,
    Esql.user_agent_values