Anthropic Session Reuse Impossible Travel
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.
Query · esql
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.*
Investigation fields
Pivot points the source recommends for triage.
user.emailEsql.distance_kmEsql.travel_kmhEsql.window_minutesEsql.country_countEsql.event_countEsql.event_id_valuesEsql.event_action_valuesEsql.source_ip_valuesEsql.source_geo_country_name_valuesEsql.source_geo_city_name_valuesEsql.first_latEsql.first_lonEsql.last_latEsql.last_lonEsql.user_agent_original_valuesEsql.anthropic_audit_actor_type_valuesEsql.timestamp_first_seenEsql.timestamp_last_seen
Known false positives
- Users on VPN or proxy egress that geo-resolves through a region distant from the user's physical location. Mobile clients on cellular networks that peer through regional hubs may geo-resolve differently than the user's location.
- Cloud egress, split-tunnel, or dual-homed clients that present different public IPs for concurrent Anthropic sessions (for example browser and API tooling) can look like impossible travel when both resolve far apart.
Analyst notes
Investigating Anthropic Session Reuse Impossible Travel
Successful user_actor activity for the same email spans distant countries too quickly for travel. Unlike login-only
impossible travel, this covers any successful audit action — useful when cookie replay or concurrent sessions produce
distant activity without a matching login.
A-B-A returns may not alert; sort raw Timeline events. No nearby magic_link_login_succeeded /
sso_login_succeeded with distant successful non-login actions strengthens a session-reuse hypothesis.
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.
Possible investigation steps
- Use
Esql.country_count, distance/speed, andEsql.event_action_valuesto see whether the distant hop was privileged (exports, role changes) vs routine chat views. - Timeline-sort events; compare IP, geo, UA, and action. Concurrent browser + tooling on far-apart cloud egress often explains FP; identical session cookies across continents without login does not.
- Check for nearby logins. Absence + privileged distant actions → treat as cookie replay until proven otherwise.
- Pair with Anthropic Impossible Travel Login / auth-failure rules when takeover is suspected.
False positive analysis
- Split-tunnel / dual-homed clients (browser + API tooling) and VPN geo noise are the main FPs — require ASN/UA corroboration.
Response and remediation
- On suspected replay/compromise: revoke sessions, reset credentials/MFA, hunt concurrent sessions, and review
compliance/export and role changes for the same
user.emailafter the distant events.