diff --git a/rules/integrations/aws/initial_access_iam_session_token_used_from_multiple_addresses.toml b/rules/integrations/aws/initial_access_iam_session_token_used_from_multiple_addresses.toml index a076f622d..6e81c88cc 100644 --- a/rules/integrations/aws/initial_access_iam_session_token_used_from_multiple_addresses.toml +++ b/rules/integrations/aws/initial_access_iam_session_token_used_from_multiple_addresses.toml @@ -2,7 +2,7 @@ creation_date = "2025/04/11" integration = ["aws"] maturity = "production" -updated_date = "2025/09/02" +updated_date = "2025/12/04" [rule] author = ["Elastic"] @@ -35,7 +35,7 @@ note = """## Triage and Analysis Access tokens are bound to a single user. Usage from multiple IP addresses may indicate the token was stolen and used elsewhere. By correlating this with additional detection criteria like multiple user agents, different cities, and different networks, we can improve the fidelity of the rule and help to eliminate false positives associated with expected behavior, like dual-stack IPV4/IPV6 usage. -#### Possible Investigation Steps +#### Possible investigation steps - **Identify the IAM User**: Examine the `aws.cloudtrail.user_identity.arn` stored in `user_id` and correlate with the `source.ips` stored in `ip_list` and `unique_ips` count to determine how widely the token was used. - **Correlate Additional Detection Context**: Examine `activity_type` and `fidelity_score` to determine additional cities, networks or user agents associated with the token usage. @@ -44,18 +44,18 @@ Access tokens are bound to a single user. Usage from multiple IP addresses may i - **Review Workload Context**: Confirm whether the user was expected to be active across multiple cities, networks or user agent environments. - **Trace Adversary Movement**: Pivot to related actions (e.g., `s3:ListBuckets`, `iam:ListUsers`, `sts:GetCallerIdentity`) to track further enumeration. -### False Positive Analysis +### False positive analysis - Automation frameworks that rotate through multiple IPs or cloud functions with dynamic egress IPs may cause this alert to fire. - Confirm geolocation and workload context before escalating. -### Response and Remediation +### Response and remediation - **Revoke the Token**: Disable or rotate the IAM credentials and invalidate the temporary session token. - **Audit the Environment**: Look for signs of lateral movement or data access during the token's validity. - **Strengthen Controls**: Require MFA for high-privilege actions, restrict access via policy conditions (e.g., IP range or device). -### References +### Additional information - [IAM Long-Term Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) - [STS Temporary Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html) @@ -99,7 +99,8 @@ from logs-aws.cloudtrail* metadata _id, _version, _index "health.amazonaws.com", "monitoring.amazonaws.com", "notifications.amazonaws.com", "ce.amazonaws.com", "cost-optimization-hub.amazonaws.com", "servicecatalog-appregistry.amazonaws.com", "securityhub.amazonaws.com", - "account.amazonaws.com", "budgets.amazonaws.com", "freetier.amazonaws.com" + "account.amazonaws.com", "budgets.amazonaws.com", "freetier.amazonaws.com", "support.amazonaws.com", + "support-console.amazonaws.com" ) | eval @@ -114,7 +115,8 @@ from logs-aws.cloudtrail* metadata _id, _version, _index Esql.source_geo_city_name = source.geo.city_name, Esql.source_network_org_name = `source.as.organization.name`, Esql.source_ip_network_pair = concat(Esql.source_ip_string, "-", `source.as.organization.name`), - Esql.event_timestamp = @timestamp + Esql.event_timestamp = @timestamp, + Esql.data_stream_namespace = data_stream.namespace | stats Esql.event_action_values = values(event.action), @@ -132,6 +134,7 @@ from logs-aws.cloudtrail* metadata _id, _version, _index Esql.user_agent_original_count_distinct = count_distinct(Esql.user_agent_original), Esql.source_geo_city_name_count_distinct = count_distinct(Esql.source_geo_city_name), Esql.source_network_org_name_count_distinct = count_distinct(Esql.source_network_org_name), + Esql.data_stream_namespace_values = values(Esql.data_stream_namespace), Esql.timestamp_first_seen = min(Esql.event_timestamp), Esql.timestamp_last_seen = max(Esql.event_timestamp), Esql.event_count = count() @@ -175,9 +178,15 @@ from logs-aws.cloudtrail* metadata _id, _version, _index Esql.source_ip_count_distinct, Esql.user_agent_original_count_distinct, Esql.source_geo_city_name_count_distinct, - Esql.source_network_org_name_count_distinct + Esql.source_network_org_name_count_distinct, + Esql.data_stream_namespace_values + +| where Esql.activity_fidelity_score == "high" + +// this rule only alerts for "high" fidelity cases, to broaden the rule scope to include all activity +// change the final condition to +// | where Esql.activity_type != "normal_activity" -| where Esql.activity_type != "normal_activity" ''' [rule.investigation_fields] @@ -201,7 +210,8 @@ field_names = [ "Esql.source_ip_count_distinct", "Esql.user_agent_original_count_distinct", "Esql.source_geo_city_name_count_distinct", - "Esql.source_network_org_name_count_distinct" + "Esql.source_network_org_name_count_distinct", + "Esql.data_stream_namespace_values" ]