GCP Vertex AI Caller Impossible Travel
Detects the same Vertex AI authenticated caller (client.user.email) generating aiplatform audit activity from geographically distant source IPs within a short window at an impossible travel speed. That pattern can indicate a stolen user or service-account credential used from multiple regions. Related: GCP Vertex AI Caller Activity From High Number of Countries covers slower multi-country reuse without a high-speed hop.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/10/02"
3integration = ["gcp_vertexai"]
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/07"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects the same Vertex AI authenticated caller (client.user.email) generating aiplatform audit activity from
13geographically distant source IPs within a short window at an impossible travel speed. That pattern can indicate a
14stolen user or service-account credential used from multiple regions. Related: GCP Vertex AI Caller Activity From
15High Number of Countries covers slower multi-country reuse without a high-speed hop.
16"""
17false_positives = [
18 """
19 Distributed CI or multi-region automation that shares one service account across distant egress IPs. Prefer
20 per-region identities, or raise distance and speed thresholds.
21 """,
22 """
23 VPN or cloud egress that geolocates incorrectly or jumps between distant POPs. Validate the IP locations before
24 rotating credentials.
25 """,
26]
27from = "now-60m"
28interval = "10m"
29language = "esql"
30license = "Elastic License v2"
31name = "GCP Vertex AI Caller Impossible Travel"
32note = """## Triage and analysis
33
34### Investigating GCP Vertex AI Caller Impossible Travel
35
36The same authenticated Vertex AI caller (`client.user.email`) generated aiplatform audit activity
37from source IPs whose geolocation implies travel faster than the rule thresholds (default at least
38500 km and 800 km/h) inside the lookback window. That pattern often indicates stolen user or
39service-account credentials used promptly from multiple regions.
40
41#### Possible investigation steps
42
43- Review `Esql.source_ip_values`, `Esql.source_geo_country_name_values`,
44 `Esql.source_geo_region_name_values`, `Esql.source_geo_city_name_values`,
45 `Esql.distance_km`, `Esql.travel_kmh`,
46 `Esql.window_minutes`, and `Esql.event_action_values` on the alert.
47- Determine whether `client.user.email` is a human user or a shared service account. Shared SAs
48 used by multi-region automation are a common false positive.
49- Timeline-sort raw audit events for that email: compare IP, geo, user agent, and action. Look for
50 privileged or unusual methods around the distant hop.
51- Check GCP IAM / audit logs for key creation, key download, or role grants for that principal near
52 the first distant event.
53- Pivot to `logs-gcp_vertexai.prompt_response_logs-*` in the same window for prompt content and
54 token volume that might show abuse after credential theft.
55- If countries accumulate to three or more over a longer window without meeting the speed
56 thresholds, review alerts from GCP Vertex AI Caller Activity From High Number of Countries.
57
58### False positive analysis
59
60- Multi-region CI or automation that shares one service account across distant egress IPs. Prefer
61 per-region identities, or raise distance/speed thresholds.
62- VPN or cloud egress that geolocates incorrectly or jumps between distant POPs. Corroborate with
63 ASN/org (`Esql.source_as_organization_name_values`) and consistent tooling user agents before
64 rotating credentials.
65
66### Response and remediation
67
68- If travel is not approved: disable or rotate the caller's keys/tokens, revoke active sessions, and
69 review IAM bindings for unexpected grants.
70- Split multi-region workloads onto region-scoped identities; enable alerts on new key creation for
71 sensitive service accounts.
72"""
73references = [
74 "https://www.elastic.co/docs/reference/integrations/gcp_vertexai",
75]
76risk_score = 47
77rule_id = "9bd6800b-a299-4c2f-978d-8219560bbee5"
78setup = """## Setup
79
80Requires GCP Vertex AI `auditlogs` with `source.ip`, `source.geo.location`, `source.geo.country_name`, and
81`client.user.email` (mapped from `protoPayload.authenticationInfo.principalEmail`).
82"""
83severity = "medium"
84tags = [
85 "Domain: GenAI",
86 "Domain: Cloud",
87 "Data Source: GCP Vertex AI",
88 "Data Source: GCP",
89 "Data Source: Google Cloud Platform",
90 "Platform: GCP",
91 "Service: GCP Vertex AI",
92 "Use Case: Threat Detection",
93 "Tactic: Credential Access",
94 "Tactic: Initial Access",
95 "Threat: Impossible Travel",
96 "Threat: LLMjacking",
97 "Threat: Unauthorized AI Usage",
98 "Mitre Atlas: AML.T0012",
99 "Mitre Atlas: AML.T0091",
100 "Mitre Atlas: AML.T0091.000",
101 "Resources: Investigation Guide",
102 "Rule Type: ES|QL",
103]
104timestamp_override = "event.ingested"
105type = "esql"
106
107query = '''
108from logs-gcp_vertexai.auditlogs-*
109| where
110 data_stream.dataset == "gcp_vertexai.auditlogs" and
111 client.user.email is not null and
112 source.ip is not null and
113 source.geo.location is not null and
114 source.geo.country_name is not null and
115 (
116 event.action like "*PredictionService.GenerateContent*" or
117 event.action like "*PredictionService.StreamGenerateContent*" or
118 event.action like "*PredictionService.CountTokens*"
119 )
120| eval
121 Esql.source_geo_lat = st_y(source.geo.location),
122 Esql.source_geo_lon = st_x(source.geo.location)
123| where Esql.source_geo_lat is not null and Esql.source_geo_lon is not null
124| stats
125 Esql.first_lat = first(Esql.source_geo_lat, @timestamp),
126 Esql.first_lon = first(Esql.source_geo_lon, @timestamp),
127 Esql.last_lat = last(Esql.source_geo_lat, @timestamp),
128 Esql.last_lon = last(Esql.source_geo_lon, @timestamp),
129 Esql.event_count = count(*),
130 Esql.country_count = count_distinct(source.geo.country_name),
131 Esql.source_ip_values = values(source.ip),
132 Esql.source_geo_country_name_values = values(source.geo.country_name),
133 Esql.source_geo_region_name_values = values(source.geo.region_name),
134 Esql.source_geo_city_name_values = values(source.geo.city_name),
135 Esql.source_as_organization_name_values = values(source.as.organization.name),
136 Esql.event_action_values = values(event.action),
137 Esql.user_agent_original_values = values(user_agent.original),
138 Esql.timestamp_first_seen = min(@timestamp),
139 Esql.timestamp_last_seen = max(@timestamp)
140 by
141 client.user.email
142| where Esql.event_count >= 2 and Esql.country_count >= 2
143| eval
144 Esql.p1 = to_geopoint(concat("POINT(", to_string(Esql.first_lon), " ", to_string(Esql.first_lat), ")")),
145 Esql.p2 = to_geopoint(concat("POINT(", to_string(Esql.last_lon), " ", to_string(Esql.last_lat), ")"))
146| eval
147 Esql.distance_km = round(st_distance(Esql.p1, Esql.p2) / 1000.0, 0),
148 Esql.window_minutes = date_diff("minute", Esql.timestamp_first_seen, Esql.timestamp_last_seen),
149 Esql.travel_kmh = case(Esql.window_minutes > 0, round(Esql.distance_km * 60.0 / Esql.window_minutes, 0), null),
150 source.ip = MV_FIRST(Esql.source_ip_values)
151| where Esql.distance_km >= 500 and Esql.travel_kmh >= 800
152| keep
153 client.user.email,
154 source.ip,
155 Esql.source_ip_values,
156 Esql.source_geo_country_name_values,
157 Esql.source_geo_region_name_values,
158 Esql.source_geo_city_name_values,
159 Esql.source_as_organization_name_values,
160 Esql.event_action_values,
161 Esql.user_agent_original_values,
162 Esql.distance_km,
163 Esql.travel_kmh,
164 Esql.window_minutes,
165 Esql.country_count,
166 Esql.event_count,
167 Esql.timestamp_first_seen,
168 Esql.timestamp_last_seen
169'''
170
171
172[[rule.threat]]
173framework = "MITRE ATT&CK"
174[[rule.threat.technique]]
175id = "T1528"
176name = "Steal Application Access Token"
177reference = "https://attack.mitre.org/techniques/T1528/"
178
179
180[rule.threat.tactic]
181id = "TA0006"
182name = "Credential Access"
183reference = "https://attack.mitre.org/tactics/TA0006/"
184[[rule.threat]]
185framework = "MITRE ATT&CK"
186[[rule.threat.technique]]
187id = "T1078"
188name = "Valid Accounts"
189reference = "https://attack.mitre.org/techniques/T1078/"
190[[rule.threat.technique.subtechnique]]
191id = "T1078.004"
192name = "Cloud Accounts"
193reference = "https://attack.mitre.org/techniques/T1078/004/"
194
195
196
197[rule.threat.tactic]
198id = "TA0001"
199name = "Initial Access"
200reference = "https://attack.mitre.org/tactics/TA0001/"
201[[rule.threat_mappings]]
202framework = "MITRE ATLAS"
203version = "2026.08"
204[[rule.threat_mappings.threat]]
205framework = "MITRE ATLAS"
206[[rule.threat_mappings.threat.technique]]
207id = "AML.T0012"
208name = "Valid Accounts"
209reference = "https://atlas.mitre.org/techniques/AML.T0012/"
210
211
212[rule.threat_mappings.threat.tactic]
213id = "AML.TA0004"
214name = "Initial Access"
215reference = "https://atlas.mitre.org/tactics/AML.TA0004/"
216[[rule.threat_mappings.threat]]
217framework = "MITRE ATLAS"
218[[rule.threat_mappings.threat.technique]]
219id = "AML.T0091"
220name = "Use Alternate Authentication Material"
221reference = "https://atlas.mitre.org/techniques/AML.T0091/"
222[[rule.threat_mappings.threat.technique.subtechnique]]
223id = "AML.T0091.000"
224name = "Application Access Token"
225reference = "https://atlas.mitre.org/techniques/AML.T0091.000/"
226
227
228
229[rule.threat_mappings.threat.tactic]
230id = "AML.TA0015"
231name = "Lateral Movement"
232reference = "https://atlas.mitre.org/tactics/AML.TA0015/"
233
234[rule.investigation_fields]
235field_names = [
236 "client.user.email",
237 "source.ip",
238 "Esql.distance_km",
239 "Esql.travel_kmh",
240 "Esql.window_minutes",
241 "Esql.country_count",
242 "Esql.event_count",
243 "Esql.source_ip_values",
244 "Esql.source_geo_country_name_values",
245 "Esql.source_geo_region_name_values",
246 "Esql.source_geo_city_name_values",
247 "Esql.source_as_organization_name_values",
248 "Esql.event_action_values",
249 "Esql.user_agent_original_values",
250 "Esql.timestamp_first_seen",
251 "Esql.timestamp_last_seen",
252]
253
254[rule.alert_suppression]
255group_by = ["source.ip"]
256missing_fields_strategy = "suppress"
257
258[rule.alert_suppression.duration]
259unit = "h"
260value = 4
Triage and analysis
Investigating GCP Vertex AI Caller Impossible Travel
The same authenticated Vertex AI caller (client.user.email) generated aiplatform audit activity
from source IPs whose geolocation implies travel faster than the rule thresholds (default at least
500 km and 800 km/h) inside the lookback window. That pattern often indicates stolen user or
service-account credentials used promptly from multiple regions.
Possible investigation steps
- Review
Esql.source_ip_values,Esql.source_geo_country_name_values,Esql.source_geo_region_name_values,Esql.source_geo_city_name_values,Esql.distance_km,Esql.travel_kmh,Esql.window_minutes, andEsql.event_action_valueson the alert. - Determine whether
client.user.emailis a human user or a shared service account. Shared SAs used by multi-region automation are a common false positive. - Timeline-sort raw audit events for that email: compare IP, geo, user agent, and action. Look for privileged or unusual methods around the distant hop.
- Check GCP IAM / audit logs for key creation, key download, or role grants for that principal near the first distant event.
- Pivot to
logs-gcp_vertexai.prompt_response_logs-*in the same window for prompt content and token volume that might show abuse after credential theft. - If countries accumulate to three or more over a longer window without meeting the speed thresholds, review alerts from GCP Vertex AI Caller Activity From High Number of Countries.
False positive analysis
- Multi-region CI or automation that shares one service account across distant egress IPs. Prefer per-region identities, or raise distance/speed thresholds.
- VPN or cloud egress that geolocates incorrectly or jumps between distant POPs. Corroborate with
ASN/org (
Esql.source_as_organization_name_values) and consistent tooling user agents before rotating credentials.
Response and remediation
- If travel is not approved: disable or rotate the caller's keys/tokens, revoke active sessions, and review IAM bindings for unexpected grants.
- Split multi-region workloads onto region-scoped identities; enable alerts on new key creation for sensitive service accounts.
References
Related rules
- GCP Vertex AI Caller Activity From High Number of Countries
- GCP Vertex AI Prompt or Response Containing Credentials
- GCP Vertex AI High GenerateContent Volume From Single IP
- GCP Vertex AI High Request and Token Volume
- GCP Vertex AI Elevated Safety Rating on Response