Detection rules › Elastic

Azure AKS Secret get or list with Suspicious User Agent

Status
production
Severity
high
Time window
9m
Author
Elastic
Source
github.com/elastic/detection-rules

Detects successful AKS (Azure Kubernetes Service) secret get or list operations where the user agent matches scripting runtimes (python, ruby, perl), command-line HTTP clients (curl, wget, HTTPie), or generic HTTP libraries (Go-http-client, okhttp, Apache-HttpClient, Guzzle, axios, undici) rather than typical kubectl or named controller traffic. Reading Kubernetes secrets with a generic client is a common credential-access step after a token or kubeconfig is stolen, and offensive tooling (for example peirates and kdigger) frequently reaches the API with a default Go HTTP client.

Known false positives

  • Approved scripts, CI jobs, or penetration tests may use generic HTTP clients. Validate change tickets and identity scope before treating as compromise. Internal automation using generic libraries can be excluded by stable service account after review.

MITRE ATT&CK coverage

TacticTechniques
Credential Access

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

[metadata]
creation_date = "2026/07/09"
integration = ["azure"]
maturity = "production"
updated_date = "2026/07/09"

[rule]
author = ["Elastic"]
description = """
Detects successful AKS (Azure Kubernetes Service) secret get or list operations where the user agent matches scripting
runtimes (python, ruby, perl), command-line HTTP clients (curl, wget, HTTPie), or generic HTTP libraries (Go-http-client,
okhttp, Apache-HttpClient, Guzzle, axios, undici) rather than typical kubectl or named controller traffic. Reading
Kubernetes secrets with a generic client is a common credential-access step after a token or kubeconfig is stolen, and
offensive tooling (for example peirates and kdigger) frequently reaches the API with a default Go HTTP client.
"""
false_positives = [
    """
    Approved scripts, CI jobs, or penetration tests may use generic HTTP clients. Validate change tickets and identity
    scope before treating as compromise. Internal automation using generic libraries can be excluded by stable service
    account after review.
    """,
]
from = "now-9m"
index = ["logs-azure.platformlogs-*"]
language = "kuery"
license = "Elastic License v2"
name = "Azure AKS Secret get or list with Suspicious User Agent"
note = """## Triage and analysis

### Investigating Azure AKS Secret get or list with Suspicious User Agent

AKS kube-audit events are carried under the flattened `azure.platformlogs.properties.log.*` subtree and share the ARM
operation `event.action: Microsoft.ContainerService/managedClusters/diagnosticLogs/Read`. This rule fires when a
successful `get` or `list` against Kubernetes `secrets` is issued with a user agent that matches scripting runtimes
(python, ruby, perl), command-line HTTP clients (curl, wget, HTTPie), or generic HTTP libraries (Go-http-client, okhttp,
Apache-HttpClient, Guzzle, axios, undici) rather than typical `kubectl` or named controller traffic. The user agent is
trivially spoofable (the telemetry shows offensive tooling masquerading as `kubectl`), so this is a corroborating
indicator, not proof.

### Possible investigation steps

- Identify the acting identity in `azure.platformlogs.properties.log.user.username` and its group memberships in
  `azure.platformlogs.properties.log.user.groups`. Control-plane and node identities (`system:apiserver`, `aksService`,
  `system:node:*`, `system:serviceaccount:kube-system:*`) are expected; a human or service-principal identity reading
  secrets with a scripted client is not.
- Confirm the request was authorized by checking
  `azure.platformlogs.properties.log.annotations.authorization.k8s.io/decision` (`allow` vs `forbid`) and the
  `azure.platformlogs.properties.log.responseStatus.code`.
- Review the targeted secret via `azure.platformlogs.properties.log.objectRef.namespace` and
  `azure.platformlogs.properties.log.objectRef.name`, and the exact API path in
  `azure.platformlogs.properties.log.requestURI`.
- Evaluate the source in `azure.platformlogs.properties.log.sourceIPs`. In-cluster control-plane traffic uses loopback
  (`::1`) or node subnet addresses; a public or pod-network source for a secret read is more suspicious. Pivot on the
  source for other API bursts, exec sessions, or RBAC changes.
- Correlate with recent Entra ID sign-ins or role assignments for the identity to determine whether the token was
  recently issued or scoped unusually.

### False positive analysis

- Approved scripts, CI jobs, or penetration tests may use generic HTTP clients. Validate identity scope before treating
  as compromise.
- Internal automation using generic libraries can be excluded by stable service account after review.
- The kubelet and controllers built on client-go can default to a `Go-http-client/2.0` user agent when no custom agent
  is set (see kubernetes/kubernetes#108726), so an operator or platform component reading secrets may match `Go-http*`.
  Exclude the specific benign identity (for example `system:serviceaccount:kube-system:*` or a validated operator
  service account) rather than removing the `Go-http*` pattern, which also catches default-user-agent offensive tooling.

### Response and remediation

- If unauthorized, revoke the identity's tokens and kubeconfig, rotate the exposed secrets, and review RBAC that permits
  secret reads.
- Hunt for downstream use of the retrieved secrets, such as new sign-ins, workload deployments, or outbound connections.
- Collect kube-audit and identity artifacts per incident response procedures.
"""
references = [
    "https://kubernetes.io/docs/concepts/configuration/secret/",
    "https://microsoft.github.io/Threat-Matrix-for-Kubernetes/",
    "https://unit42.paloaltonetworks.com/hildegard-malware-teamtnt/",
    "https://unit42.paloaltonetworks.com/modern-kubernetes-threats/",
    "https://www.sysdig.com/blog/teamtnt-kubelet-credentials",
    "https://github.com/kubernetes/kubernetes/issues/108726",
]
risk_score = 73
rule_id = "4eaf5dd4-c511-4d0e-b66d-e218e6e3da76"
setup = """
The Azure Fleet integration collecting AKS diagnostic logs forwarded through Event Hub into the `azure.platformlogs`
data stream is required for this rule. The `kube-audit` log category is required specifically: secret get/list are
read operations, which the `kube-audit-admin` category excludes, so a cluster shipping only `kube-audit-admin` is blind
to this rule.
"""
severity = "high"
tags = [
    "Domain: Cloud",
    "Domain: Kubernetes",
    "Data Source: Azure",
    "Data Source: Azure Platform Logs",
    "Data Source: Kubernetes",
    "Use Case: Threat Detection",
    "Tactic: Credential Access",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "query"

query = '''
data_stream.dataset:azure.platformlogs and
  event.action:"Microsoft.ContainerService/managedClusters/diagnosticLogs/Read" and
  azure.platformlogs.category:"kube-audit" and
  azure.platformlogs.properties.log.responseStatus.code:("200" or "201") and
  azure.platformlogs.properties.log.verb:("get" or "list") and
  azure.platformlogs.properties.log.objectRef.resource:"secrets" and
  azure.platformlogs.properties.log.userAgent:(
    curl* or python* or Python* or wget* or Wget* or Go-http* or perl* or libwww-perl* or
    java* or Java* or node* or php* or Guzzle* or Bun* or axios* or undici* or okhttp* or
    Apache-HttpClient* or HTTPie* or Ruby* or PostmanRuntime* or RestSharp* or *distrib#kali* or *kali-amd64* or *kali-arm64*
  )
'''

[rule.investigation_fields]
field_names = [
    "@timestamp",
    "event.action",
    "azure.platformlogs.category",
    "azure.platformlogs.properties.log.verb",
    "azure.platformlogs.properties.log.user.username",
    "azure.platformlogs.properties.log.userAgent",
    "azure.platformlogs.properties.log.sourceIPs",
    "azure.platformlogs.properties.log.objectRef.resource",
    "azure.platformlogs.properties.log.objectRef.namespace",
    "azure.platformlogs.properties.log.objectRef.name",
    "azure.platformlogs.properties.log.responseStatus.code",
]

[[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/"

Stages and Predicates

Stage 1: query

data_stream.dataset:azure.platformlogs and
  event.action:"Microsoft.ContainerService/managedClusters/diagnosticLogs/Read" and
  azure.platformlogs.category:"kube-audit" and
  azure.platformlogs.properties.log.responseStatus.code:("200" or "201") and
  azure.platformlogs.properties.log.verb:("get" or "list") and
  azure.platformlogs.properties.log.objectRef.resource:"secrets" and
  azure.platformlogs.properties.log.userAgent:(
    curl* or python* or Python* or wget* or Wget* or Go-http* or perl* or libwww-perl* or
    java* or Java* or node* or php* or Guzzle* or Bun* or axios* or undici* or okhttp* or
    Apache-HttpClient* or HTTPie* or Ruby* or PostmanRuntime* or RestSharp* or *distrib#kali* or *kali-amd64* or *kali-arm64*
  )

Indicators

These rows show field, operator, and value matches.