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/09/18"
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 "Noise: Medium",
77 "Performance: Fast",
78 "Rule Type: ES|QL",
79 "Platform: GCP",
80 "Domain: Containers",
81 "Platform: Kubernetes",
82]
83timestamp_override = "event.ingested"
84type = "esql"
85
86query = '''
87from logs-gcp.audit-* metadata _id, _index, _version
88| where data_stream.dataset == "gcp.audit"
89 and service.name == "k8s.io"
90 and event.action in (
91 "io.k8s.core.v1.namespaces.get",
92 "io.k8s.core.v1.namespaces.list",
93 "io.k8s.core.v1.nodes.get",
94 "io.k8s.core.v1.nodes.list",
95 "io.k8s.core.v1.pods.get",
96 "io.k8s.core.v1.pods.list",
97 "io.k8s.core.v1.configmaps.get",
98 "io.k8s.core.v1.configmaps.list",
99 "io.k8s.core.v1.serviceaccounts.get",
100 "io.k8s.core.v1.serviceaccounts.list",
101 "io.k8s.authorization.rbac.v1.roles.get",
102 "io.k8s.authorization.rbac.v1.roles.list",
103 "io.k8s.authorization.rbac.v1.rolebindings.get",
104 "io.k8s.authorization.rbac.v1.rolebindings.list",
105 "io.k8s.authorization.rbac.v1.clusterroles.get",
106 "io.k8s.authorization.rbac.v1.clusterroles.list",
107 "io.k8s.authorization.rbac.v1.clusterrolebindings.get",
108 "io.k8s.authorization.rbac.v1.clusterrolebindings.list"
109 )
110 and source.ip is not null
111 and client.user.email is not null
112 and not to_string(source.ip) in ("127.0.0.1", "::1")
113 and not client.user.email like "system:kube-*"
114 and not client.user.email like "system:gke-*"
115 and not client.user.email like "system:node:*"
116 and not client.user.email like "system:serviceaccount:kube-system:*"
117 and not client.user.email like "system:serviceaccount:gke-managed*"
118 and not client.user.email in (
119 "system:apiserver",
120 "system:addon-manager",
121 "system:kubestore-collector",
122 "gcp:kube-bootstrap",
123 "system:serviceaccount:security:trivy-operator"
124 )
125 and not client.user.email like "system:serviceaccount:flux-system:*"
126 and not client.user.email like "system:serviceaccount:argocd:*"
127 and not client.user.email like "system:serviceaccount:argocd-system:*"
128 and not client.user.email like "system:serviceaccount:cattle-turtles-system:*"
129 and not client.user.email like "system:serviceaccount:*:palette-manager"
130| eval Esql.time_interval = date_trunc(1 minute, @timestamp),
131 Esql.resource_kind = case(
132 event.action in ("io.k8s.core.v1.namespaces.get", "io.k8s.core.v1.namespaces.list"), "namespaces",
133 event.action in ("io.k8s.core.v1.nodes.get", "io.k8s.core.v1.nodes.list"), "nodes",
134 event.action in ("io.k8s.core.v1.pods.get", "io.k8s.core.v1.pods.list"), "pods",
135 event.action in ("io.k8s.core.v1.configmaps.get", "io.k8s.core.v1.configmaps.list"), "configmaps",
136 event.action in ("io.k8s.core.v1.serviceaccounts.get", "io.k8s.core.v1.serviceaccounts.list"), "serviceaccounts",
137 event.action in ("io.k8s.authorization.rbac.v1.roles.get", "io.k8s.authorization.rbac.v1.roles.list"), "roles",
138 event.action in ("io.k8s.authorization.rbac.v1.rolebindings.get", "io.k8s.authorization.rbac.v1.rolebindings.list"), "rolebindings",
139 event.action in ("io.k8s.authorization.rbac.v1.clusterroles.get", "io.k8s.authorization.rbac.v1.clusterroles.list"), "clusterroles",
140 event.action in ("io.k8s.authorization.rbac.v1.clusterrolebindings.get", "io.k8s.authorization.rbac.v1.clusterrolebindings.list"), "clusterrolebindings",
141 null
142 ),
143 Esql.is_rbac = case(
144 event.action in (
145 "io.k8s.authorization.rbac.v1.roles.get",
146 "io.k8s.authorization.rbac.v1.roles.list",
147 "io.k8s.authorization.rbac.v1.rolebindings.get",
148 "io.k8s.authorization.rbac.v1.rolebindings.list",
149 "io.k8s.authorization.rbac.v1.clusterroles.get",
150 "io.k8s.authorization.rbac.v1.clusterroles.list",
151 "io.k8s.authorization.rbac.v1.clusterrolebindings.get",
152 "io.k8s.authorization.rbac.v1.clusterrolebindings.list"
153 ),
154 1,
155 0
156 ),
157 Esql.is_layout = case(
158 event.action in (
159 "io.k8s.core.v1.namespaces.get",
160 "io.k8s.core.v1.namespaces.list",
161 "io.k8s.core.v1.pods.get",
162 "io.k8s.core.v1.pods.list",
163 "io.k8s.core.v1.nodes.get",
164 "io.k8s.core.v1.nodes.list"
165 ),
166 1,
167 0
168 )
169| stats
170 Esql.unique_resources = count_distinct(Esql.resource_kind),
171 Esql.rbac_event_count = sum(Esql.is_rbac),
172 Esql.layout_event_count = sum(Esql.is_layout),
173 Esql.enumerated_resources = values(Esql.resource_kind),
174 Esql.enumerated_namespaces = values(orchestrator.namespace),
175 Esql.enumerated_resource_names = values(gcp.audit.resource_name),
176 Esql.event_outcome_values = values(event.outcome)
177 by client.user.email, source.ip, user_agent.original, Esql.time_interval
178| where Esql.unique_resources >= 5
179 and Esql.rbac_event_count > 0
180 and Esql.layout_event_count > 0
181| keep Esql.*, client.user.email, source.ip, user_agent.original
182'''
183
184[rule.alert_suppression]
185group_by = ["client.user.email", "source.ip"]
186duration = {value = 30, unit = "m"}
187missing_fields_strategy = "suppress"
188
189[[rule.threat]]
190framework = "MITRE ATT&CK"
191
192[[rule.threat.technique]]
193id = "T1613"
194name = "Container and Resource Discovery"
195reference = "https://attack.mitre.org/techniques/T1613/"
196
197[rule.threat.tactic]
198id = "TA0007"
199name = "Discovery"
200reference = "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 Sensitive RBAC Change Followed by Workload Modification
- GKE Anonymous Endpoint Permission Enumeration
- GKE Certificate Signing Request Self-Approved
- GKE Container Created with Excessive Linux Capabilities