AWS EC2 Bulk User Data Enumeration
editAWS EC2 Bulk User Data Enumeration
editIdentifies 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.
Rule type: esql
Rule indices: None
Severity: high
Risk score: 73
Runs every: 5m
Searches indices from: now-10m (Date Math format, see also Additional look-back time)
Maximum alerts per execution: 100
References:
Tags:
- Domain: Cloud
- Data Source: AWS
- Data Source: Amazon Web Services
- Data Source: AWS CloudTrail
- Platform: AWS
- Service: AWS EC2
- Use Case: Threat Detection
- Tactic: Discovery
- Tactic: Credential Access
- Resources: Investigation Guide
- Rule Type: ES|QL
Version: 1
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
editTriage and analysis
Investigating AWS EC2 Bulk User Data Enumeration
EC2 userData is returned by DescribeInstanceAttribute when attribute is userData. It is frequently used for
bootstrap scripts and sometimes contains credentials or tokens. This rule alerts when one principal
(aws.cloudtrail.user_identity.arn), identity type, source IP, and source ASN reads that attribute for more than 20
distinct aws.cloudtrail.request_parameters values in the 10-minute lookback. For this API those values correspond to
distinct instance requests.
Possible investigation steps
-
Review
aws.cloudtrail.user_identity.arnandaws.cloudtrail.user_identity.type. Confirm whether this identity is expected to read instance user data at fleet scale. -
Review
Esql.count_distinct_instance_idandEsql.aws_request_parameters_values. Extract theinstanceIdvalues and check whether the instances belong to sensitive workloads. -
Review
source.ip,source.as.organization.name, andEsql.user_agent_values. Unexpected ASNs or clients such as the AWS CLI or an SDK from an unfamiliar network are higher priority than a known automation host. -
Search CloudTrail for the same principal and source IP in the surrounding hour for
ModifyInstanceAttribute,RunInstances,GetPasswordData,AssumeRole, and IAM changes. - If the instances are still available, inspect their current user data for secrets. Prefer AWS Secrets Manager or Parameter Store over credentials stored in user data.
False positive analysis
-
Approved CSPM and inventory tools. This rule already excludes principal names matching CrowdStrike CSPM, Orca
Security, Aikido Security, Stream Security (
streamsec-*), andAWSReservedSSO_Poweruser. - Calls invoked by AWS Internal, CloudFormation, AI DevOps, EMR, or AIOps.
- Terraform user agents whose source ASN is Amazon, Google, Microsoft, Verizon, or AT&T.
- Add further exceptions by principal ARN or user agent when the behavior is documented and recurring.
Response and remediation
- If the activity is not authorized, revoke the role session or disable the access key and restrict the source IP.
- Rotate any credentials found in the retrieved user data and remove secrets from instance user data.
-
Limit
ec2:DescribeInstanceAttributeto principals that need it.
Setup
editSetup
The AWS integration must collect CloudTrail management events into logs-aws.cloudtrail-*.
DescribeInstanceAttribute is a management event logged by default. source.ip and
source.as.organization.name must be populated, because events missing either field are excluded.
Rule query
editfrom 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
Framework: MITRE ATT&CKTM
-
Tactic:
- Name: Discovery
- ID: TA0007
- Reference URL: https://attack.mitre.org/tactics/TA0007/
-
Technique:
- Name: Cloud Infrastructure Discovery
- ID: T1580
- Reference URL: https://attack.mitre.org/techniques/T1580/
-
Tactic:
- Name: Credential Access
- ID: TA0006
- Reference URL: https://attack.mitre.org/tactics/TA0006/
-
Technique:
- Name: Unsecured Credentials
- ID: T1552
- Reference URL: https://attack.mitre.org/techniques/T1552/
-
Sub-technique:
- Name: Cloud Instance Metadata API
- ID: T1552.005
- Reference URL: https://attack.mitre.org/techniques/T1552/005/