Detection rules › Elastic

Kubernetes Service Account Token Created via TokenRequest API

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

Detects the creation of a Kubernetes service account token through the TokenRequest API by a non-system identity. The TokenRequest API allows users and workloads to programmatically generate short-lived tokens for any service account they have create permissions on, without accessing the filesystem or the mounted projected token. Attackers who have gained initial access to a cluster can abuse this API to mint tokens for more privileged service accounts, pivot to cloud provider resources via IRSA/workload identity, or generate long-lived tokens that persist beyond pod termination. Unlike mounted service account tokens which are detectable through file access monitoring, tokens created via the TokenRequest API leave no filesystem footprint, they are only visible in Kubernetes audit logs as a create verb on the serviceaccounts/token subresource. This rule excludes legitimate system components such as the kubelet, kube-controller-manager, and cloud provider managed identities (EKS, AKS, GKE) that routinely create tokens for pod lifecycle management.

MITRE ATT&CK coverage

TacticTechniques
Credential AccessT1552.007 Unsecured Credentials: Container API

Event coverage

ProviderEventTitle
Kubernetes-serviceaccountscreate-serviceaccounts-tokencreate serviceaccounts/token

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body elastic

[metadata]
creation_date = "2026/05/05"
integration = ["kubernetes"]
maturity = "production"
updated_date = "2026/05/05"

[rule]
author = ["Elastic"]
description = """
Detects the creation of a Kubernetes service account token through the TokenRequest API by a non-system identity. The
TokenRequest API allows users and workloads to programmatically generate short-lived tokens for any service account
they have create permissions on, without accessing the filesystem or the mounted projected token. Attackers who have
gained initial access to a cluster can abuse this API to mint tokens for more privileged service accounts, pivot to
cloud provider resources via IRSA/workload identity, or generate long-lived tokens that persist beyond pod
termination. Unlike mounted service account tokens which are detectable through file access monitoring, tokens created
via the TokenRequest API leave no filesystem footprint, they are only visible in Kubernetes audit logs as a create
verb on the serviceaccounts/token subresource. This rule excludes legitimate system components such as the kubelet,
kube-controller-manager, and cloud provider managed identities (EKS, AKS, GKE) that routinely create tokens for pod
lifecycle management.
"""
false_positives = [
    """
    New automation, admission webhooks, or platform agents that legitimately call the TokenRequest API under a
    non-standard user string may require narrow exclusions by user.name or source IP.
    """,
]
from = "now-6m"
index = ["logs-kubernetes.audit_logs-*"]
language = "kuery"
license = "Elastic License v2"
name = "Kubernetes Service Account Token Created via TokenRequest API"
note = """## Triage and analysis

### Investigating Kubernetes Service Account Token Created via TokenRequest API

This alert indicates a successful `create` against the `serviceaccounts/token` subresource (TokenRequest API), which
issues a new service account token without a filesystem read. In EKS and other managed clusters, this can be abused to
mint tokens for more privileged service accounts (including IRSA-linked ones) and pivot to cloud APIs.

#### What to review first

- Actor and origin:
  - `user.name` / `kubernetes.audit.user.username`
  - `source.ip` / `kubernetes.audit.sourceIPs`
  - `user_agent.original` / `kubernetes.audit.userAgent`
  - For cloud identity, review `kubernetes.audit.user.extra.*` (e.g., `arn`, `principalId`).
- Targeted service account:
  - `kubernetes.audit.objectRef.namespace` and `kubernetes.audit.objectRef.name`
  - `kubernetes.audit.requestURI` (should resemble `/api/v1/namespaces/<ns>/serviceaccounts/<sa>/token`)
- Token issuance hints:
  - `kubernetes.audit.annotations.authentication_kubernetes_io/issued-credential-id` (token JTI/issued credential id)

#### Scoping

- Identify which Role/ClusterRoleBindings grant the actor `create` on `serviceaccounts/token` in the affected namespace.
- Pivot on the same `user.name` and `source.ip` for follow-on secret reads, pod exec, RBAC changes, or cloud API calls.

### Response and remediation

- If unauthorized, remove/revert the RBAC permission that allows TokenRequest (`serviceaccounts/token`) and rotate the
  affected service account credentials where applicable.
- For IRSA/workload identity cases, rotate/revoke the cloud role session pathways and review cloud audit logs for API
  activity from the time window of the token mint.
"""
references = [
    "https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-v1/",
    "https://attack.mitre.org/techniques/T1552/007/",
]
risk_score = 47
rule_id = "4df91789-7859-4bc4-9c5a-6b56bfa81a8b"
severity = "medium"
tags = [
    "Data Source: Kubernetes",
    "Domain: Kubernetes",
    "Use Case: Threat Detection",
    "Tactic: Credential Access",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "query"
query = '''
data_stream.dataset:"kubernetes.audit_logs" and 
kubernetes.audit.verb:"create" and
kubernetes.audit.objectRef.resource:"serviceaccounts" and
kubernetes.audit.objectRef.subresource:"token" and
user.name:(* and not 
 (system\:kube-controller-manager or
  system\:kube-scheduler or
  system\:node\:* or
  system\:serviceaccount\:kube-system\:* or
  eks\:* or
  aksService or
  aks-service or
  masterclient or
  nodeclient or
  system\:serviceaccount\:gke-managed-system\:* or
  system\:serviceaccount\:gke-connect\:* or
  system\:serviceaccount\:anthos-identity-service\:* or
  system\:gke-controller-manager or
  system\:serviceaccount\:tigera-operator\:* or
  system\:serviceaccount\:calico-system\:*))
'''

[[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:"kubernetes.audit_logs" and 
kubernetes.audit.verb:"create" and
kubernetes.audit.objectRef.resource:"serviceaccounts" and
kubernetes.audit.objectRef.subresource:"token" and
user.name:(* and not 
 (system\:kube-controller-manager or
  system\:kube-scheduler or
  system\:node\:* or
  system\:serviceaccount\:kube-system\:* or
  eks\:* or
  aksService or
  aks-service or
  masterclient or
  nodeclient or
  system\:serviceaccount\:gke-managed-system\:* or
  system\:serviceaccount\:gke-connect\:* or
  system\:serviceaccount\:anthos-identity-service\:* or
  system\:gke-controller-manager or
  system\:serviceaccount\:tigera-operator\:* or
  system\:serviceaccount\:calico-system\:*))

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
user.nameeqaks-service
user.nameeqaksService
user.nameeqmasterclient
user.nameeqnodeclient
user.nameeqsystem:gke-controller-manager
user.nameeqsystem:kube-controller-manager
user.nameeqsystem:kube-scheduler
user.namestarts_witheks\:
user.namestarts_withsystem\:node\:
user.namestarts_withsystem\:serviceaccount\:anthos-identity-service\:
user.namestarts_withsystem\:serviceaccount\:calico-system\:
user.namestarts_withsystem\:serviceaccount\:gke-connect\:
user.namestarts_withsystem\:serviceaccount\:gke-managed-system\:
user.namestarts_withsystem\:serviceaccount\:kube-system\:
user.namestarts_withsystem\:serviceaccount\:tigera-operator\:

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
data_stream.dataseteq
  • kubernetes.audit_logs
kubernetes.audit.objectRef.resourceeq
  • serviceaccounts
kubernetes.audit.objectRef.subresourceeq
  • token
kubernetes.audit.verbeq
  • create
user.nameis_not_null
  • (no value, null check)