Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
creation_date = "2025/04/11"
integration = ["aws"]
maturity = "production"
updated_date = "2025/07/16"
updated_date = "2025/09/02"

[rule]
author = ["Elastic"]
Expand Down Expand Up @@ -86,16 +86,20 @@ from logs-aws.cloudtrail* metadata _id, _version, _index
and aws.cloudtrail.user_identity.arn is not null
and aws.cloudtrail.user_identity.type == "IAMUser"
and source.ip is not null
and aws.cloudtrail.user_identity.access_key_id is not null
and not (
user_agent.original like "%Terraform%" or
user_agent.original like "%Ansible%" or
user_agent.original like "%Pulumni%"
user_agent.original like "*Terraform*" or
user_agent.original like "*Ansible*" or
user_agent.original like "*Pulumi*"
)
and `source.as.organization.name` != "AMAZON-AES"
and not ((
`source.as.organization.name` == "AMAZON-02" and aws.cloudtrail.event_category == "Data"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making sure: are these backticks supported in ES|QL pipeline?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and they used to be necessary for any field with as in the name since that means something in ESQL. We used to get errors when including this field without the backticks but I actually just tried it again without the backticks and am not getting those errors anymore so something might have changed with the language ability to recognize fields like this. I'm going to keep the backticks though as they are still supported.

and event.provider not in (
"health.amazonaws.com", "monitoring.amazonaws.com", "notifications.amazonaws.com",
"ce.amazonaws.com", "cost-optimization-hub.amazonaws.com",
"servicecatalog-appregistry.amazonaws.com", "securityhub.amazonaws.com"
"servicecatalog-appregistry.amazonaws.com", "securityhub.amazonaws.com",
"account.amazonaws.com", "budgets.amazonaws.com", "freetier.amazonaws.com"
)

| eval
Expand All @@ -108,8 +112,9 @@ from logs-aws.cloudtrail* metadata _id, _version, _index
Esql.source_ip_user_agent_pair = concat(Esql.source_ip_string, " - ", user_agent.original),
Esql.source_ip_city_pair = concat(Esql.source_ip_string, " - ", source.geo.city_name),
Esql.source_geo_city_name = source.geo.city_name,
Esql.event_timestamp = @timestamp,
Esql.source_network_org_name = `source.as.organization.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

| stats
Esql.event_action_values = values(event.action),
Expand All @@ -122,6 +127,7 @@ from logs-aws.cloudtrail* metadata _id, _version, _index
Esql.source_geo_city_name_values = values(Esql.source_geo_city_name),
Esql.source_ip_city_pair_values = values(Esql.source_ip_city_pair),
Esql.source_network_org_name_values = values(Esql.source_network_org_name),
Esql.source_ip_network_pair_values = values(Esql.source_ip_network_pair),
Esql.source_ip_count_distinct = count_distinct(Esql.source_ip),
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),
Expand Down Expand Up @@ -165,6 +171,7 @@ from logs-aws.cloudtrail* metadata _id, _version, _index
Esql.source_geo_city_name_values,
Esql.source_ip_city_pair_values,
Esql.source_network_org_name_values,
Esql.source_ip_network_pair_values,
Esql.source_ip_count_distinct,
Esql.user_agent_original_count_distinct,
Esql.source_geo_city_name_count_distinct,
Expand All @@ -173,6 +180,30 @@ from logs-aws.cloudtrail* metadata _id, _version, _index
| where Esql.activity_type != "normal_activity"
'''

[rule.investigation_fields]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we have used dynamic fields in timelines before and what the implications are. With the best practices and guidelines being followed, key:value pairs like Esql.source_ip_values may show up in timelines whereas Esql.activity_fidelity_score is likely unique to this rule. That would only be for alerts too, not raw telemetry and may not be valuable to pivoting and triaging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, they seem to be supported in the UI when modifying the rule, but I agree we should double check before merging.

Screenshot 2025-09-04 at 12 04 55 PM

field_names = [
"Esql.timestamp_first_seen",
"Esql.timestamp_last_seen",
"Esql.activity_type",
"Esql.activity_fidelity_score",
"Esql.event_count",
"Esql.aws_cloudtrail_user_identity_arn_values",
"Esql.aws_cloudtrail_user_identity_access_key_id_values",
"Esql.event_action_values",
"Esql.event_provider_values",
"Esql.source_ip_values",
"Esql.user_agent_original_values",
"Esql.source_ip_user_agent_pair_values",
"Esql.source_geo_city_name_values",
"Esql.source_ip_city_pair_values",
"Esql.source_network_org_name_values",
"Esql.source_ip_network_pair_values",
"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"
]


[[rule.threat]]
framework = "MITRE ATT&CK"
Expand Down
Loading