GKE Certificate Signing Request Self-Approved

Last updated 12 days ago on 2026-07-10
Created 12 days ago on 2026-07-10

About

Detects when the same non-system GKE identity creates a CertificateSigningRequest (CSR) and then approves that same CSR within five minutes, consistent with self-approval abuse. Attackers who gain CSR create and approval RBAC can submit a certificate request and approve it themselves to obtain a long-lived client certificate without involving cluster operators, a pattern documented in Kubernetes persistence research and adversary emulation.
Tags
Domain: CloudDomain: KubernetesData Source: GCPData Source: GCP Audit LogsData Source: Google Cloud PlatformUse Case: Threat DetectionTactic: PersistenceTactic: Privilege EscalationLanguage: esql
Severity
high
Risk Score
73
MITRE ATT&CK™

Persistence (TA0003)(external, opens in a new tab or window)

Privilege Escalation (TA0004)(external, opens in a new tab or window)

False Positive Examples
Automation that both submits and approves CSRs in one workflow may trigger this rule. Baseline cert-manager or internal PKI pipelines and tune exclusions for known service accounts.
License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

gcp(external, opens in a new tab or window)

Query
text code block:
from logs-gcp.audit-* metadata _id, _index, _version | where data_stream.dataset == "gcp.audit" and service.name == "k8s.io" and event.outcome == "success" and event.action in ( "io.k8s.certificates.v1.certificatesigningrequests.create", "io.k8s.certificates.v1.certificatesigningrequests.approval.update" ) and client.user.email is not null and gcp.audit.resource_name is not null and not client.user.email in ( "system:gcp-controller-manager", "system:kube-controller-manager", "system:serviceaccount:kube-system:certificate-controller", "kubelet-bootstrap", "kubelet-nodepool-bootstrap" ) and not client.user.email like "system:node:*" | eval Esql.csr_name = replace(gcp.audit.resource_name, "/approval", "") | stats Esql.create_count = count(*) where event.action == "io.k8s.certificates.v1.certificatesigningrequests.create", Esql.approval_count = count(*) where event.action == "io.k8s.certificates.v1.certificatesigningrequests.approval.update", Esql.event_action_values = values(event.action), Esql.timestamp_first_seen = min(@timestamp), Esql.timestamp_last_seen = max(@timestamp), Esql.source_ip_values = values(source.ip), Esql.user_agent_original_values = values(user_agent.original), Esql.data_stream_namespace_values = values(data_stream.namespace) by client.user.email, Esql.csr_name | where Esql.create_count >= 1 and Esql.approval_count >= 1 and date_diff("seconds", Esql.timestamp_first_seen, Esql.timestamp_last_seen) <= 300 | keep client.user.email, Esql.*

Install detection rules in Elastic Security

Detect GKE Certificate Signing Request Self-Approved in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(external, opens in a new tab or window).