Sample rules
OpenAI ChatGPT Enterprise User Activity From High Number of Countries
- source: elastic
- technicques:
- T1078
- T1539
Description
Detects ChatGPT Enterprise activity for the same user email from at least three countries and two unique user agents within a four-hour interval across any compliance data stream. Stolen session cookies often produce post-auth activity (conversation views, deletions, downloads) without a new login event, so this rule is not limited to authentication logs.
Detection logic
from logs-openai_chatgpt_enterprise.*
| where
user.email is not null and
source.ip is not null and
source.geo.country_iso_code is not null
| stats
Esql.country_count = count_distinct(source.geo.country_iso_code),
Esql.region_count = count_distinct(source.geo.region_name),
Esql.source_ip_count = count_distinct(source.ip),
Esql.user_agent_count = count_distinct(user_agent.original),
Esql.event_count = count(*),
Esql.event_action_values = values(event.action),
Esql.source_ip_values = values(source.ip),
Esql.source_geo_country_iso_code_values = values(source.geo.country_iso_code),
Esql.source_geo_region_name_values = values(source.geo.region_name),
Esql.source_geo_city_name_values = values(source.geo.city_name),
Esql.user_agent_original_values = values(user_agent.original),
Esql.timestamp_first_seen = min(@timestamp),
Esql.timestamp_last_seen = max(@timestamp)
by user.email
| where Esql.country_count >= 3 and Esql.user_agent_count >= 2
| keep user.email, Esql.*
Anthropic Session Reuse Impossible Travel
- source: elastic
- technicques:
- T1078
- T1539
Description
Detects successful Anthropic audit activity for the same user email from source IP addresses whose query-time geo-locations (via IP_LOCATION) span at least two countries, are separated by at least 500 km, and imply travel faster than 800 km/h within a short (~15-minute) lookback. Unlike login-only impossible travel, this rule covers any successful user-actor activity and can surface session cookie replay or concurrent session reuse when no new authentication events appear.
Detection logic
from logs-anthropic.audit-*
| where
data_stream.dataset == "anthropic.audit" and
event.outcome == "success" and
anthropic.audit.actor.type == "user_actor" and
user.email is not null and
source.ip is not null
| IP_LOCATION geo = source.ip with { "properties": ["country_name", "city_name", "location"] }
| eval
Esql.source_geo_lat = st_y(geo.location),
Esql.source_geo_lon = st_x(geo.location)
| where Esql.source_geo_lat is not null and Esql.source_geo_lon is not null
| stats
Esql.first_lat = first(Esql.source_geo_lat, @timestamp),
Esql.first_lon = first(Esql.source_geo_lon, @timestamp),
Esql.last_lat = last(Esql.source_geo_lat, @timestamp),
Esql.last_lon = last(Esql.source_geo_lon, @timestamp),
Esql.event_count = count(*),
Esql.country_count = count_distinct(geo.country_name),
Esql.event_id_values = values(event.id),
Esql.event_action_values = values(event.action),
Esql.source_ip_values = values(source.ip),
Esql.source_geo_country_name_values = values(geo.country_name),
Esql.source_geo_city_name_values = values(geo.city_name),
Esql.user_agent_original_values = values(user_agent.original),
Esql.anthropic_audit_actor_type_values = values(anthropic.audit.actor.type),
Esql.timestamp_first_seen = min(@timestamp),
Esql.timestamp_last_seen = max(@timestamp)
by user.email
| where Esql.event_count >= 2 and Esql.country_count >= 2
| eval
Esql.p1 = to_geopoint(concat("POINT(", to_string(Esql.first_lon), " ", to_string(Esql.first_lat), ")")),
Esql.p2 = to_geopoint(concat("POINT(", to_string(Esql.last_lon), " ", to_string(Esql.last_lat), ")"))
| eval
Esql.distance_km = round(st_distance(Esql.p1, Esql.p2) / 1000.0, 0),
Esql.window_minutes = date_diff("minute", Esql.timestamp_first_seen, Esql.timestamp_last_seen),
Esql.travel_kmh = case(Esql.window_minutes > 0, round(Esql.distance_km * 60.0 / Esql.window_minutes, 0), null)
| where Esql.distance_km >= 500 and Esql.travel_kmh >= 800
| keep user.email, Esql.*
Anthropic User Activity From High Number of Countries
- source: elastic
- technicques:
- T1078
- T1539
Description
Detects Anthropic activity for the same user email from at least three countries and two unique user agents within a four-hour interval. This highly unusual activity may indicate leaked session cookies being abused by a remote adversary using a VPN.
Detection logic
FROM logs-anthropic.audit-*
| where user.email IS NOT NULL and source.ip IS NOT NULL and `user.email` != "unknown@invalid.internal"
| IP_LOCATION geo = source.ip with { "properties": ["country_name", "city_name", "location"] }
| stats Esql.country_count = COUNT_DISTINCT(geo.country_name),
Esql.source_ip_count = COUNT_DISTINCT(source.ip),
Esql.user_agent_count = COUNT_DISTINCT(`user_agent`.original),
Esql.event_count = count(*),
Esql.event_action_values = values(event.action),
Esql.source_ip_values = values(source.ip),
Esql.source_geo_country_name_values = values(geo.country_name),
Esql.source_geo_city_name_values = values(geo.city_name),
Esql.user_agent_original_values = values(user_agent.original),
Esql.timestamp_first_seen = min(@timestamp),
Esql.timestamp_last_seen = max(@timestamp) by user.email
| where Esql.country_count >= 3 AND Esql.user_agent_count >= 2 AND
(MV_CONTAINS(Esql.event_action_values, "claude_chat_created") or MV_CONTAINS(Esql.event_action_values, "claude_chat_deleted") or MV_CONTAINS(Esql.event_action_values, "claude_user_settings_updated") or MV_CONTAINS(Esql.event_action_values, "claude_file_deleted") or MV_CONTAINS(Esql.event_action_values, "claude_chat_updated"))
| Keep user.email, Esql.*