Anthropic User Activity From High Number of Countries

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.

Rule type: esql
Rule indices:

Rule Severity: critical
Risk Score: 99
Runs every: 5m
Searches indices from: now-4h
Maximum alerts per execution: 100
References:

Tags:

  • Domain: GenAI
  • Domain: Identity
  • Platform: Anthropic
  • Data Source: Anthropic Audit Logs
  • Use Case: Identity and Access Audit
  • Use Case: Threat Detection
  • Resources: Investigation Guide
  • Rule Type: ES|QL
  • Tactic: Credential Access
  • Tactic: Initial Access
  • Mitre Atlas: AML.T0012
  • Threat: Impossible Travel

Version: 1
Rule authors:

  • Elastic

Rule license: Elastic License v2

Successful user_actor activity for the same email spans distant countries too quickly for travel.

Escalate when countries differ, UAs diverge (browser vs curl/python), login events are absent, or admin/export actions appear on the distant IP. Close as FP for documented dual-homed VPN/cloud egress with consistent corporate UAs.

  • Use Esql.source_geo_country_name_values to verify the user's activity history by country.
  • Use Esql.event_action_values to verify the performed actions.
  • Contact the user to verify if he is using VPN.
  • Check for nearby logins. Absence + privileged distant actions → treat as cookie replay until proven otherwise.
  • Split-tunnel / dual-homed clients (browser + API tooling) and VPN geo noise are the main FPs — require ASN/UA corroboration.
  • On suspected replay/compromise: force logout for the target user from all connected devices, revoke sessions, reset credentials/MFA, hunt concurrent sessions, and review compliance/export and role changes for the same user.email after the distant events.
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.*
		

Framework: MITRE ATT&CK

Framework: MITRE ATT&CK