Detection rules › Elastic
GKE Unusual Service Account Secret Access via New User Agent
Detects the first successful GKE secrets.get by a pod service account from a previously unseen combination of service-account identity, user agent, and source IP. Controllers routinely read secrets with a stable client fingerprint; a new user agent or source for that service account could indicate a stolen token used outside the workload (for example curl, a custom script, or kubectl from an unexpected host).
Known false positives
- Controller upgrades, sidecar replacements, or client library version bumps change user agents and can produce a first-seen alert for known service accounts. Confirm the new client is expected before treating as compromise.
- Newly deployed operators or workloads that change egress IP will alert once while the history window learns their client fingerprint.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Kubernetes | get-secrets: get secrets |
Rules detecting the same action
These rules filter on the same operation.
- Azure AKS Secret get or list with Suspicious User Agent (Elastic)
- Direct Interactive Kubernetes API Request by Unusual Utilities (Elastic)
- GKE Rapid Secret GET Activity Against Multiple Objects (Elastic)
- GKE Secret Access from Node or Denied Service Account (Elastic)
- GKE Secret Access via Unusual User Agent (Elastic)
- GKE Secret get or list with Suspicious User Agent (Elastic)
- K8s Secret Get Successfully (Falco)
- K8s Secret Get Unsuccessfully Tried (Falco)
Rule body
[metadata]
creation_date = "2026/07/17"
integration = ["gcp"]
maturity = "production"
updated_date = "2026/07/31"
[rule]
author = ["Elastic"]
description = """
Detects the first successful GKE secrets.get by a pod service account from a previously unseen combination of
service-account identity, user agent, and source IP. Controllers routinely read secrets with a stable client
fingerprint; a new user agent or source for that service account could indicate a stolen token used outside
the workload (for example curl, a custom script, or kubectl from an unexpected host).
"""
false_positives = [
"""
Controller upgrades, sidecar replacements, or client library version bumps change user agents and can produce
a first-seen alert for known service accounts. Confirm the new client is expected before treating as compromise.
""",
"""
Newly deployed operators or workloads that change egress IP will alert once while the history window learns
their client fingerprint.
""",
]
from = "now-6m"
index = ["logs-gcp.audit-*"]
language = "kuery"
license = "Elastic License v2"
name = "GKE Unusual Service Account Secret Access via New User Agent"
note = """## Triage and analysis
### Investigating GKE Unusual Service Account Secret Access via New User Agent
This new-terms rule alerts when a `system:serviceaccount:*` identity successfully calls `secrets.get` with a
`client.user.email` + `user_agent.original` + `source.ip` combination not seen in the history window.
### Possible investigation steps
- Confirm whether the service account normally uses this user agent or whether the client looks like an interactive
or scripting tool (`curl`, `python`, `kubectl`, generic HTTP libraries).
- Review `gcp.audit.resource_name` and namespace scope against the workload's expected secret mounts and RBAC.
- Pivot on the same `client.user.email` or `source.ip` for secret list/get bursts, exec, or RBAC changes.
- Compare to recent deployments or operator upgrades that would legitimately introduce a new client string.
### False positive analysis
- Operator or library upgrades that change the user-agent string for an otherwise unchanged service account.
- First enablement of the rule will surface baseline controller clients until the history window fills.
### Response and remediation
- If malicious, revoke the service-account token, rotate exposed secrets, isolate the originating workload or
host, and tighten RBAC so the identity can only read required secrets.
"""
setup = "The GCP Fleet integration with GKE audit logs enabled is required to be compatible with this rule."
references = [
"https://kubernetes.io/docs/reference/access-authn-authz/authentication/#service-account-tokens",
]
risk_score = 21
rule_id = "5dd3358c-4664-4b4c-aca1-b3fa4a4c83c9"
severity = "low"
tags = [
"Domain: Cloud",
"Domain: Kubernetes",
"Data Source: GCP",
"Data Source: Google Cloud Platform",
"Use Case: Threat Detection",
"Tactic: Credential Access",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "new_terms"
query = '''
data_stream.dataset:gcp.audit and service.name:k8s.io and event.outcome:success and
event.action:"io.k8s.core.v1.secrets.get" and
client.user.email:system\:serviceaccount\:* and
user_agent.original:(* and not *kubernetes/$Format*) and
source.ip:(* and not (127.0.0.1 or "::1"))
'''
[rule.new_terms]
field = "new_terms_fields"
value = ["client.user.email", "user_agent.original", "source.ip"]
[[rule.new_terms.history_window_start]]
field = "history_window_start"
value = "now-7d"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1552"
name = "Unsecured Credentials"
reference = "https://attack.mitre.org/techniques/T1552/"
[[rule.threat.technique.subtechnique]]
id = "T1552.007"
name = "Container API"
reference = "https://attack.mitre.org/techniques/T1552/007/"
[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"
[rule.investigation_fields]
field_names = [
"@timestamp",
"client.user.email",
"source.ip",
"user_agent.original",
"event.action",
"event.outcome",
"gcp.audit.resource_name",
"data_stream.namespace",
]
Stages and Predicates
Stage 1: new_terms
data_stream.dataset:gcp.audit and service.name:k8s.io and event.outcome:success and
event.action:"io.k8s.core.v1.secrets.get" and
client.user.email:system\:serviceaccount\:* and
user_agent.original:(* and not *kubernetes/$Format*) and
source.ip:(* and not (127.0.0.1 or "::1"))
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
source.ip | in | 127.0.0.1, ::1 | excludes:source.ip field:"source.ip" value:"127.0.0.1" field:"source.ip" value:"::1" |
user_agent.original | match | kubernetes/$Format | excludes:user_agent.original field:"user_agent.original" value:"kubernetes/$Format" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
client.user.email | wildcard |
| field:"client.user.email" kind:wildcard value:"system:serviceaccount:*" |
data_stream.dataset | eq |
| field:"data_stream.dataset" kind:eq value:"gcp.audit" |
event.action | eq |
| field:"EventType" kind:eq value:"io.k8s.core.v1.secrets.get" |
event.outcome | eq |
| field:"event.outcome" kind:eq value:"success" |
service.name | eq |
| field:"ServiceName" kind:eq value:"k8s.io" |
source.ip | is_not_null | field:"src_ip" kind:is_not_null | |
user_agent.original | is_not_null | field:"aws::userAgent" kind:is_not_null |