GKE Multi-Resource Discovery
Adversaries who land credentials in a GKE cluster—or abuse an over-privileged token, often map the environment before exfiltration or privilege escalation. A practical first pass is to learn where workloads run, how the cluster is partitioned, and what RBAC exists at namespace vs cluster scope. Rapid get/list traffic across many distinct API resource kinds that answer those questions (namespaces, workloads, roles, cluster-wide roles) is a common setup and orientation pattern for both interactive attackers and automated recon scripts. This rule highlights that cross-resource burst from a single client fingerprint within a one-minute bucket when both cluster-layout and RBAC resource kinds are touched, so analysts can separate routine automation from potential discovery ahead of follow-on actions.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/07/21"
3integration = ["gcp"]
4maturity = "production"
5updated_date = "2026/07/31"
6
7[rule]
8author = ["Elastic"]
9description = """
10Adversaries who land credentials in a GKE cluster—or abuse an over-privileged token, often map the environment before
11exfiltration or privilege escalation. A practical first pass is to learn where workloads run, how the cluster is
12partitioned, and what RBAC exists at namespace vs cluster scope. Rapid get/list traffic across many distinct API
13resource kinds that answer those questions (namespaces, workloads, roles, cluster-wide roles) is a common setup and
14orientation pattern for both interactive attackers and automated recon scripts. This rule highlights that
15cross-resource burst from a single client fingerprint within a one-minute bucket when both cluster-layout and RBAC
16resource kinds are touched, so analysts can separate routine automation from potential discovery ahead of follow-on
17actions.
18"""
19false_positives = [
20 """
21 Platform operators, installers, or runbooks that reconcile RBAC and workload state may span these resource types
22 in a short window; tune by identity, source IP, or user agent when documented.
23 """,
24 """
25 GitOps controllers and cluster scanners can still match if not covered by built-in exclusions; baseline approved
26 service accounts after review.
27 """,
28]
29from = "now-6m"
30interval = "5m"
31language = "esql"
32license = "Elastic License v2"
33name = "GKE Multi-Resource Discovery"
34note = """## Triage and analysis
35
36### Investigating GKE Multi-Resource Discovery
37
38The rule groups GKE audit get/list events on namespaces, nodes, pods, configmaps, serviceaccounts, roles,
39rolebindings, clusterroles, and clusterrolebindings into one-minute windows per `client.user.email`, `source.ip`,
40and `user_agent.original`. It alerts when five or more distinct resource kinds appear and the burst includes both
41cluster-layout kinds (namespaces, pods, or nodes) and RBAC kinds. Allowed and denied authorizations are both
42included: failures still signal probing.
43
44### Possible investigation steps
45
46- Review `Esql.enumerated_resources`, `Esql.enumerated_namespaces`, and `Esql.enumerated_resource_names` for
47 ordering and targeted APIs.
48- Confirm whether `source.ip` and `user_agent.original` match expected admin or automation clients.
49- Correlate with follow-on secret reads, RoleBinding changes, pod exec, or unusual user agents from the same actor.
50
51### False positive analysis
52
53- Documented platform sync jobs that read layout and RBAC together; exclude known service accounts after validation.
54- Upgrade or install windows that briefly query many resource kinds; correlate with change records.
55
56### Response and remediation
57
58- If malicious, revoke or rotate the implicated credentials, tighten RBAC, and inspect for data access or persistence
59 established after the burst.
60"""
61setup = "The GCP Fleet integration with GKE audit logs enabled is required to be compatible with this rule."
62references = [
63 "https://microsoft.github.io/Threat-Matrix-for-Kubernetes/",
64]
65risk_score = 47
66rule_id = "0302d3b2-1892-4448-a805-980aa4a36ba3"
67severity = "medium"
68tags = [
69 "Domain: Cloud",
70 "Domain: Kubernetes",
71 "Data Source: GCP",
72 "Data Source: Google Cloud Platform",
73 "Use Case: Threat Detection",
74 "Tactic: Discovery",
75 "Resources: Investigation Guide",
76]
77timestamp_override = "event.ingested"
78type = "esql"
79
80query = '''
81from logs-gcp.audit-* metadata _id, _index, _version
82| where data_stream.dataset == "gcp.audit"
83 and service.name == "k8s.io"
84 and event.action in (
85 "io.k8s.core.v1.namespaces.get",
86 "io.k8s.core.v1.namespaces.list",
87 "io.k8s.core.v1.nodes.get",
88 "io.k8s.core.v1.nodes.list",
89 "io.k8s.core.v1.pods.get",
90 "io.k8s.core.v1.pods.list",
91 "io.k8s.core.v1.configmaps.get",
92 "io.k8s.core.v1.configmaps.list",
93 "io.k8s.core.v1.serviceaccounts.get",
94 "io.k8s.core.v1.serviceaccounts.list",
95 "io.k8s.authorization.rbac.v1.roles.get",
96 "io.k8s.authorization.rbac.v1.roles.list",
97 "io.k8s.authorization.rbac.v1.rolebindings.get",
98 "io.k8s.authorization.rbac.v1.rolebindings.list",
99 "io.k8s.authorization.rbac.v1.clusterroles.get",
100 "io.k8s.authorization.rbac.v1.clusterroles.list",
101 "io.k8s.authorization.rbac.v1.clusterrolebindings.get",
102 "io.k8s.authorization.rbac.v1.clusterrolebindings.list"
103 )
104 and source.ip is not null
105 and client.user.email is not null
106 and not to_string(source.ip) in ("127.0.0.1", "::1")
107 and not client.user.email like "system:kube-*"
108 and not client.user.email like "system:gke-*"
109 and not client.user.email like "system:node:*"
110 and not client.user.email like "system:serviceaccount:kube-system:*"
111 and not client.user.email like "system:serviceaccount:gke-managed*"
112 and not client.user.email in (
113 "system:apiserver",
114 "system:addon-manager",
115 "system:kubestore-collector",
116 "gcp:kube-bootstrap",
117 "system:serviceaccount:security:trivy-operator"
118 )
119 and not client.user.email like "system:serviceaccount:flux-system:*"
120 and not client.user.email like "system:serviceaccount:argocd:*"
121 and not client.user.email like "system:serviceaccount:argocd-system:*"
122 and not client.user.email like "system:serviceaccount:cattle-turtles-system:*"
123 and not client.user.email like "system:serviceaccount:*:palette-manager"
124| eval Esql.time_interval = date_trunc(1 minute, @timestamp),
125 Esql.resource_kind = case(
126 event.action in ("io.k8s.core.v1.namespaces.get", "io.k8s.core.v1.namespaces.list"), "namespaces",
127 event.action in ("io.k8s.core.v1.nodes.get", "io.k8s.core.v1.nodes.list"), "nodes",
128 event.action in ("io.k8s.core.v1.pods.get", "io.k8s.core.v1.pods.list"), "pods",
129 event.action in ("io.k8s.core.v1.configmaps.get", "io.k8s.core.v1.configmaps.list"), "configmaps",
130 event.action in ("io.k8s.core.v1.serviceaccounts.get", "io.k8s.core.v1.serviceaccounts.list"), "serviceaccounts",
131 event.action in ("io.k8s.authorization.rbac.v1.roles.get", "io.k8s.authorization.rbac.v1.roles.list"), "roles",
132 event.action in ("io.k8s.authorization.rbac.v1.rolebindings.get", "io.k8s.authorization.rbac.v1.rolebindings.list"), "rolebindings",
133 event.action in ("io.k8s.authorization.rbac.v1.clusterroles.get", "io.k8s.authorization.rbac.v1.clusterroles.list"), "clusterroles",
134 event.action in ("io.k8s.authorization.rbac.v1.clusterrolebindings.get", "io.k8s.authorization.rbac.v1.clusterrolebindings.list"), "clusterrolebindings",
135 null
136 ),
137 Esql.is_rbac = case(
138 event.action in (
139 "io.k8s.authorization.rbac.v1.roles.get",
140 "io.k8s.authorization.rbac.v1.roles.list",
141 "io.k8s.authorization.rbac.v1.rolebindings.get",
142 "io.k8s.authorization.rbac.v1.rolebindings.list",
143 "io.k8s.authorization.rbac.v1.clusterroles.get",
144 "io.k8s.authorization.rbac.v1.clusterroles.list",
145 "io.k8s.authorization.rbac.v1.clusterrolebindings.get",
146 "io.k8s.authorization.rbac.v1.clusterrolebindings.list"
147 ),
148 1,
149 0
150 ),
151 Esql.is_layout = case(
152 event.action in (
153 "io.k8s.core.v1.namespaces.get",
154 "io.k8s.core.v1.namespaces.list",
155 "io.k8s.core.v1.pods.get",
156 "io.k8s.core.v1.pods.list",
157 "io.k8s.core.v1.nodes.get",
158 "io.k8s.core.v1.nodes.list"
159 ),
160 1,
161 0
162 )
163| stats
164 Esql.unique_resources = count_distinct(Esql.resource_kind),
165 Esql.rbac_event_count = sum(Esql.is_rbac),
166 Esql.layout_event_count = sum(Esql.is_layout),
167 Esql.enumerated_resources = values(Esql.resource_kind),
168 Esql.enumerated_namespaces = values(orchestrator.namespace),
169 Esql.enumerated_resource_names = values(gcp.audit.resource_name),
170 Esql.event_outcome_values = values(event.outcome)
171 by client.user.email, source.ip, user_agent.original, Esql.time_interval
172| where Esql.unique_resources >= 5
173 and Esql.rbac_event_count > 0
174 and Esql.layout_event_count > 0
175| keep Esql.*, client.user.email, source.ip, user_agent.original
176'''
177
178[rule.alert_suppression]
179group_by = ["client.user.email", "source.ip"]
180duration = {value = 30, unit = "m"}
181missing_fields_strategy = "suppress"
182
183[[rule.threat]]
184framework = "MITRE ATT&CK"
185
186[[rule.threat.technique]]
187id = "T1613"
188name = "Container and Resource Discovery"
189reference = "https://attack.mitre.org/techniques/T1613/"
190
191[rule.threat.tactic]
192id = "TA0007"
193name = "Discovery"
194reference = "https://attack.mitre.org/tactics/TA0007/"
Triage and analysis
Investigating GKE Multi-Resource Discovery
The rule groups GKE audit get/list events on namespaces, nodes, pods, configmaps, serviceaccounts, roles,
rolebindings, clusterroles, and clusterrolebindings into one-minute windows per client.user.email, source.ip,
and user_agent.original. It alerts when five or more distinct resource kinds appear and the burst includes both
cluster-layout kinds (namespaces, pods, or nodes) and RBAC kinds. Allowed and denied authorizations are both
included: failures still signal probing.
Possible investigation steps
- Review
Esql.enumerated_resources,Esql.enumerated_namespaces, andEsql.enumerated_resource_namesfor ordering and targeted APIs. - Confirm whether
source.ipanduser_agent.originalmatch expected admin or automation clients. - Correlate with follow-on secret reads, RoleBinding changes, pod exec, or unusual user agents from the same actor.
False positive analysis
- Documented platform sync jobs that read layout and RBAC together; exclude known service accounts after validation.
- Upgrade or install windows that briefly query many resource kinds; correlate with change records.
Response and remediation
- If malicious, revoke or rotate the implicated credentials, tighten RBAC, and inspect for data access or persistence established after the burst.
References
Related rules
- GKE Endpoint Permission Enumeration
- GKE Anonymous Endpoint Permission Enumeration
- GKE Forbidden Request from Unusual User Agent
- GKE API Server Proxying Request to Kubelet
- GKE API Request Failure Burst by User