OpenAI ChatGPT Enterprise Impossible Travel Login
Detects successful ChatGPT Enterprise logins for the same user email from source IPs whose geo-locations are separated by at least 1,000 km, with implied travel faster than 800 km/h, within a 24-hour window. That pattern can indicate account sharing, VPN or proxy egress mismatches, or an adversary authenticating from a geography far from the legitimate user's baseline.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/10/06"
3integration = ["openai_chatgpt_enterprise"]
4maturity = "production"
5min_stack_comments = "ES|QL FIRST/LAST aggregations and st_distance require 9.4.0+."
6min_stack_version = "9.4.0"
7updated_date = "2026/10/06"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects successful ChatGPT Enterprise logins for the same user email from source IPs whose geo-locations are
13separated by at least 1,000 km, with implied travel faster than 800 km/h, within a 24-hour window. That pattern can
14indicate account sharing, VPN or proxy egress mismatches, or an adversary authenticating from a geography far from
15the legitimate user's baseline.
16"""
17false_positives = [
18 "Users on VPN or proxy egress that geo-resolves through a region distant from the user physical location.",
19 "Mobile clients on cellular networks that peer through regional hubs may geo-resolve differently than the user location.",
20]
21from = "now-24h"
22interval = "1h"
23language = "esql"
24license = "Elastic License v2"
25name = "OpenAI ChatGPT Enterprise Impossible Travel Login"
26note = """## Triage and analysis
27
28### Investigating OpenAI ChatGPT Enterprise Impossible Travel Login
29
30Two successful ChatGPT Enterprise logins for the same `user.email` appear too far apart, too quickly, to be physical travel. That can indicate account sharing, VPN or proxy geo mismatch, or adversary authentication from a distant egress.
31
32Note: this detection compares the first and last successful login locations in the lookback window. An A-B-A return to the original location can under-report distance, so always timeline-sort raw authentication events before concluding.
33
34Escalate when the distant IP or user agent is unfamiliar, the user denies travel or VPN use, or privileged activity follows the distant sign-in. Close as a false positive when the user confirms known travel, split home and office VPN use, or geo-database noise with matching corporate clients.
35
36#### Possible investigation steps
37
38- Review `Esql.distance_km`, `Esql.travel_kmh`, `Esql.window_minutes`, and the geo and IP value lists.
39- Timeline-sort successful logins and compare each `source.ip`, geo region, and `user_agent.original`.
40- Pair with multi-country authentication activity or privileged audit actions after the distant sign-in.
41- Contact the user only after geo and user-agent triage.
42
43### False positive analysis
44
45- VPN pools and anycast or satellite geo misplacements commonly inflate implied travel speed. Corroborate with ASN and user agent before escalating.
46
47### Response and remediation
48
49- On suspected compromise: revoke sessions, reset credentials and MFA, and review admin or data-access activity after the distant sign-in.
50"""
51references = [
52 "https://www.elastic.co/docs/reference/integrations/openai_chatgpt_enterprise",
53 "https://platform.openai.com/docs/compliance",
54]
55risk_score = 73
56rule_id = "b9e509ba-ca0f-450a-8577-0b0a1098d598"
57setup = """## Setup
58
59This rule requires the OpenAI ChatGPT Enterprise integration to collect compliance logs into Elasticsearch.
60
61https://www.elastic.co/docs/reference/integrations/openai_chatgpt_enterprise
62"""
63severity = "high"
64tags = [
65 "Domain: GenAI",
66 "Platform: OpenAI",
67 "Data Source: OpenAI ChatGPT Enterprise",
68 "Service: OpenAI ChatGPT Enterprise",
69 "Use Case: Threat Detection",
70 "Resources: Investigation Guide",
71 "Rule Type: ES|QL",
72 "Domain: Identity",
73 "Use Case: Identity and Access Audit",
74 "Tactic: Initial Access",
75 "Mitre Atlas: AML.T0012",
76 "Threat: Impossible Travel",
77]
78timestamp_override = "event.ingested"
79type = "esql"
80
81query = '''
82from logs-openai_chatgpt_enterprise.auth_log-*
83| where
84 data_stream.dataset == "openai_chatgpt_enterprise.auth_log" and
85 event.action in ("login_success", "native_login_success") and
86 user.email is not null and
87 source.ip is not null and
88 source.geo.location is not null and
89 source.geo.country_iso_code is not null
90| eval
91 Esql.source_geo_lat = st_y(source.geo.location),
92 Esql.source_geo_lon = st_x(source.geo.location)
93| where Esql.source_geo_lat is not null and Esql.source_geo_lon is not null
94| stats
95 Esql.first_lat = first(Esql.source_geo_lat, @timestamp),
96 Esql.first_lon = first(Esql.source_geo_lon, @timestamp),
97 Esql.last_lat = last(Esql.source_geo_lat, @timestamp),
98 Esql.last_lon = last(Esql.source_geo_lon, @timestamp),
99 Esql.event_count = count(*),
100 Esql.country_count = count_distinct(source.geo.country_iso_code),
101 Esql.event_action_values = values(event.action),
102 Esql.source_ip_values = values(source.ip),
103 Esql.source_geo_country_iso_code_values = values(source.geo.country_iso_code),
104 Esql.source_geo_region_name_values = values(source.geo.region_name),
105 Esql.source_geo_city_name_values = values(source.geo.city_name),
106 Esql.user_agent_original_values = values(user_agent.original),
107 Esql.timestamp_first_seen = min(@timestamp),
108 Esql.timestamp_last_seen = max(@timestamp)
109 by user.email
110| where Esql.event_count >= 2 and Esql.country_count >= 2
111| eval
112 Esql.p1 = to_geopoint(concat("POINT(", to_string(Esql.first_lon), " ", to_string(Esql.first_lat), ")")),
113 Esql.p2 = to_geopoint(concat("POINT(", to_string(Esql.last_lon), " ", to_string(Esql.last_lat), ")"))
114| eval
115 Esql.distance_km = round(st_distance(Esql.p1, Esql.p2) / 1000.0, 0),
116 Esql.window_minutes = date_diff("minute", Esql.timestamp_first_seen, Esql.timestamp_last_seen),
117 Esql.travel_kmh = case(Esql.window_minutes > 0, round(Esql.distance_km * 60.0 / Esql.window_minutes, 0), null)
118| where Esql.distance_km >= 1000 and Esql.travel_kmh >= 800
119| keep user.email, Esql.*
120'''
121
122
123[[rule.threat]]
124framework = "MITRE ATT&CK"
125[[rule.threat.technique]]
126id = "T1078"
127name = "Valid Accounts"
128reference = "https://attack.mitre.org/techniques/T1078/"
129[[rule.threat.technique.subtechnique]]
130id = "T1078.004"
131name = "Cloud Accounts"
132reference = "https://attack.mitre.org/techniques/T1078/004/"
133
134
135[rule.threat.tactic]
136id = "TA0001"
137name = "Initial Access"
138reference = "https://attack.mitre.org/tactics/TA0001/"
139
140[[rule.threat_mappings]]
141framework = "MITRE ATLAS"
142version = "2026.08"
143[[rule.threat_mappings.threat]]
144framework = "MITRE ATLAS"
145[[rule.threat_mappings.threat.technique]]
146id = "AML.T0012"
147name = "Valid Accounts"
148reference = "https://atlas.mitre.org/techniques/AML.T0012/"
149
150
151[rule.threat_mappings.threat.tactic]
152id = "AML.TA0004"
153name = "Initial Access"
154reference = "https://atlas.mitre.org/tactics/AML.TA0004/"
155
156[rule.alert_suppression]
157group_by = ["user.email"]
158missing_fields_strategy = "suppress"
159
160[rule.alert_suppression.duration]
161unit = "h"
162value = 24
163
164[rule.investigation_fields]
165field_names = [
166 "user.email",
167 "Esql.distance_km",
168 "Esql.travel_kmh",
169 "Esql.window_minutes",
170 "Esql.event_count",
171 "Esql.country_count",
172 "Esql.source_ip_values",
173 "Esql.source_geo_country_iso_code_values",
174 "Esql.source_geo_region_name_values",
175 "Esql.source_geo_city_name_values",
176 "Esql.user_agent_original_values",
177 "Esql.timestamp_first_seen",
178 "Esql.timestamp_last_seen",
179]
Triage and analysis
Investigating OpenAI ChatGPT Enterprise Impossible Travel Login
Two successful ChatGPT Enterprise logins for the same user.email appear too far apart, too quickly, to be physical travel. That can indicate account sharing, VPN or proxy geo mismatch, or adversary authentication from a distant egress.
Note: this detection compares the first and last successful login locations in the lookback window. An A-B-A return to the original location can under-report distance, so always timeline-sort raw authentication events before concluding.
Escalate when the distant IP or user agent is unfamiliar, the user denies travel or VPN use, or privileged activity follows the distant sign-in. Close as a false positive when the user confirms known travel, split home and office VPN use, or geo-database noise with matching corporate clients.
Possible investigation steps
- Review
Esql.distance_km,Esql.travel_kmh,Esql.window_minutes, and the geo and IP value lists. - Timeline-sort successful logins and compare each
source.ip, geo region, anduser_agent.original. - Pair with multi-country authentication activity or privileged audit actions after the distant sign-in.
- Contact the user only after geo and user-agent triage.
False positive analysis
- VPN pools and anycast or satellite geo misplacements commonly inflate implied travel speed. Corroborate with ASN and user agent before escalating.
Response and remediation
- On suspected compromise: revoke sessions, reset credentials and MFA, and review admin or data-access activity after the distant sign-in.
References
Related rules
- OpenAI ChatGPT Enterprise User Activity From High Number of Countries
- OpenAI ChatGPT Enterprise Account Owner Role Assigned
- OpenAI ChatGPT Enterprise Authentication from a Suspicious User Agent
- OpenAI ChatGPT Enterprise Domain Created
- OpenAI ChatGPT Enterprise Multiple Authentication Failures