Kubernetes Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected

This rule detects potential endpoint enumeration attempts by an anonymous user. An anonymous user is a user that is not authenticated or authorized to access the Kubernetes API server. By looking for a series of failed API requests, on multiple endpoints, and a limited number of documents, this rule can detect automated permission enumeration attempts. This behavior is uncommon for regular Kubernetes clusters.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/02/02"
  3integration = ["kubernetes"]
  4maturity = "production"
  5updated_date = "2026/02/09"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10This rule detects potential endpoint enumeration attempts by an anonymous user. An anonymous user is a user that
 11is not authenticated or authorized to access the Kubernetes API server. By looking for a series of failed API requests,
 12on multiple endpoints, and a limited number of documents, this rule can detect automated permission enumeration attempts.
 13This behavior is uncommon for regular Kubernetes clusters.
 14"""
 15language = "esql"
 16license = "Elastic License v2"
 17name = "Kubernetes Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected"
 18note = """ ## Triage and analysis
 19
 20> **Disclaimer**:
 21> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
 22
 23### Investigating Kubernetes Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected
 24
 25This detects a burst of Kubernetes API requests from an unauthenticated identity that probes many different endpoints and resource types, producing mostly forbidden/unauthorized/not found responses within a small window. It matters because this pattern maps the cluster’s exposed surface and reveals which APIs might be reachable before an attacker commits to credential theft or exploitation. A common usage pattern is scripted GET/LIST sweeps across core and custom resources (for example pods, secrets, namespaces, and CRDs) from one source IP and user agent.
 26
 27### Possible investigation steps
 28
 29- Review the specific request URIs and resource types queried and their sequence to fingerprint common reconnaissance tooling and whether high-value endpoints (e.g., secrets, tokenreviews, subjectaccessreviews, CRDs) were probed.  
 30- Determine whether the apparent source IP is internal or Internet-routable and confirm the true originating client by correlating load balancer/ingress/firewall logs (including X-Forwarded-For) with the audit event timestamps.  
 31- Validate Kubernetes API server authentication/authorization posture during the window to identify misconfiguration that permits anonymous access and confirm whether any requests returned successful responses that indicate real data exposure.  
 32- Hunt for follow-on activity from the same origin or user agent such as authenticated requests, service account token usage, RBAC/ClusterRoleBinding changes, pod exec, or secret/configmap reads to assess escalation beyond discovery.  
 33- If the API endpoint is publicly reachable, apply immediate containment by restricting network access to the API server (allowlisting, VPN/private endpoint, temporary IP blocks) while preserving relevant audit and network logs for forensics.
 34
 35### False positive analysis
 36
 37- Misconfigured or transitional API server authentication (e.g., anonymous auth briefly enabled or a failing authn proxy/fronting component) can cause legitimate clients to appear as `system:anonymous` and generate multiple 401/403/404 responses across several endpoints during normal cluster access attempts.
 38- Internal cluster health checks or component discovery behavior that hits multiple API paths without presenting credentials (or uses requests that the audit log records with empty/null usernames) can resemble enumeration when it produces a short burst of failed requests across diverse resources from a single source IP and user agent.
 39
 40### Response and remediation
 41
 42- Immediately restrict Kubernetes API server network exposure by allowlisting known admin/VPN IPs and temporarily blocking the observed source IP(s) and user agent at the load balancer/firewall while preserving audit logs and reverse-proxy access logs for the timeframe.  
 43- Eradicate the anonymous access path by disabling anonymous authentication on the API server, fixing any misconfigured auth proxy that forwards unauthenticated traffic, and removing any RBAC bindings that grant permissions to `system:anonymous` or `system:unauthenticated`.  
 44- Validate whether any requests from the same source returned successful responses (especially reads of secrets/configmaps, tokenreviews/subjectaccessreviews, or CRDs) and, if so, rotate impacted service account tokens and credentials and perform a targeted review of recently issued tokens and new ClusterRoleBindings.  
 45- Recover by re-enabling API access in a controlled manner (private endpoint/VPN, bastion, or mTLS), confirming expected kubectl and controller functionality, and monitoring for renewed bursts of failed requests across many request URIs from unauthenticated identities.  
 46- Escalate to the incident response lead and platform security team if any anonymous request succeeded, if the probing repeats from multiple external IPs, or if follow-on activity appears (new privileged RBAC, pod exec, or secret reads) within 24 hours of the enumeration attempt.  
 47- Harden by enforcing least-privilege RBAC, enabling and retaining full audit logging for authn/authz failures, applying API server rate limits/WAF rules for repeated 401/403/404 sweeps, and continuously validating that the API endpoint is not publicly reachable.
 48"""
 49references = [
 50    "https://heilancoos.github.io/research/2025/12/16/kubernetes.html#unauthenticated-api-access"
 51]
 52risk_score = 47
 53rule_id = "2dd0d4fd-0cc9-4d18-8b46-1a507e28bbc0"
 54severity = "medium"
 55tags = [
 56  "Data Source: Kubernetes",
 57  "Domain: Kubernetes",
 58  "Use Case: Threat Detection",
 59  "Tactic: Discovery",
 60  "Resources: Investigation Guide",
 61]
 62timestamp_override = "event.ingested"
 63type = "esql"
 64query = '''
 65from logs-kubernetes.audit_logs-* metadata _id, _index, _version
 66| where (
 67    kubernetes.audit.user.username in ("system:anonymous", "system:unauthenticated") or
 68    kubernetes.audit.user.username is null or
 69    kubernetes.audit.user.username == ""
 70  ) and
 71  kubernetes.audit.level in ("RequestResponse", "ResponseComplete", "Request")
 72
 73| eval Esql.decision = `kubernetes.audit.annotations.authorization_k8s_io/decision`
 74| eval Esql.code = kubernetes.audit.responseStatus.code
 75
 76| eval Esql.outcome = case(
 77    Esql.decision == "allow", "authz_allow",
 78    Esql.decision == "forbid", "authz_forbid",
 79
 80    // fallback: infer from status when decision is missing
 81    Esql.code in (401, 403), "authn_authz_failed",
 82    (Esql.code >= 200 and Esql.code < 300), "success",
 83    Esql.code == 404, "not_found",
 84    Esql.code is null, "unknown",
 85    true, "other_error"
 86  )
 87
 88| stats
 89    Esql.document_count = count(),
 90
 91    Esql.authz_allow_count = sum(case(Esql.outcome == "authz_allow", 1, 0)),
 92    Esql.authz_forbid_count = sum(case(Esql.outcome == "authz_forbid", 1, 0)),
 93
 94    Esql.status_fail_count = sum(case(Esql.outcome == "authn_authz_failed", 1, 0)),
 95    Esql.success_count = sum(case(Esql.outcome == "success", 1, 0)),
 96    Esql.not_found_count = sum(case(Esql.outcome == "not_found", 1, 0)),
 97    Esql.other_error_count = sum(case(Esql.outcome == "other_error", 1, 0)),
 98    Esql.unknown_count = sum(case(Esql.outcome == "unknown", 1, 0)),
 99
100    Esql.kubernetes_audit_verb_count_distinct = count_distinct(kubernetes.audit.verb),
101    Esql.kubernetes_audit_requestURI_count_distinct = count_distinct(kubernetes.audit.requestURI),
102    Esql.kubernetes_audit_objectRef_resource_count_distinct = count_distinct(kubernetes.audit.objectRef.resource),
103
104    Esql.kubernetes_audit_outcome_values = values(Esql.outcome),
105    Esql.kubernetes_audit_decision_values = values(Esql.decision),
106    Esql.kubernetes_audit_responseStatus_code_values = values(Esql.code),
107    Esql.kubernetes_audit_responseStatus_message_values = values(kubernetes.audit.responseStatus.message),
108
109    Esql.kubernetes_audit_verb_values = values(kubernetes.audit.verb),
110    Esql.kubernetes_audit_objectRef_resource_values = values(kubernetes.audit.objectRef.resource),
111    Esql.kubernetes_audit_objectRef_namespace_values = values(kubernetes.audit.objectRef.namespace),
112    Esql.kubernetes_audit_user_username_values = values(kubernetes.audit.user.username),
113    Esql.kubernetes_audit_user_groups_values = values(kubernetes.audit.user.groups),
114    Esql.kubernetes_audit_requestURI_values = values(kubernetes.audit.requestURI),
115    Esql.data_stream_namespace_values = values(data_stream.namespace)
116
117  BY kubernetes.audit.sourceIPs, kubernetes.audit.userAgent
118
119| where
120    Esql.kubernetes_audit_requestURI_count_distinct > 5 and
121    Esql.kubernetes_audit_objectRef_resource_count_distinct > 3 and
122    Esql.document_count < 50 and
123    (Esql.authz_forbid_count >= 1 or Esql.status_fail_count >= 1 or Esql.not_found_count >= 3)
124
125| keep Esql.*, kubernetes.audit.sourceIPs, kubernetes.audit.userAgent
126'''
127
128[[rule.threat]]
129framework = "MITRE ATT&CK"
130
131[[rule.threat.technique]]
132id = "T1613"
133name = "Container and Resource Discovery"
134reference = "https://attack.mitre.org/techniques/T1613/"
135
136[rule.threat.tactic]
137id = "TA0007"
138name = "Discovery"
139reference = "https://attack.mitre.org/tactics/TA0007/"

Triage and analysis

Disclaimer: This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.

Investigating Kubernetes Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected

This detects a burst of Kubernetes API requests from an unauthenticated identity that probes many different endpoints and resource types, producing mostly forbidden/unauthorized/not found responses within a small window. It matters because this pattern maps the cluster’s exposed surface and reveals which APIs might be reachable before an attacker commits to credential theft or exploitation. A common usage pattern is scripted GET/LIST sweeps across core and custom resources (for example pods, secrets, namespaces, and CRDs) from one source IP and user agent.

Possible investigation steps

  • Review the specific request URIs and resource types queried and their sequence to fingerprint common reconnaissance tooling and whether high-value endpoints (e.g., secrets, tokenreviews, subjectaccessreviews, CRDs) were probed.
  • Determine whether the apparent source IP is internal or Internet-routable and confirm the true originating client by correlating load balancer/ingress/firewall logs (including X-Forwarded-For) with the audit event timestamps.
  • Validate Kubernetes API server authentication/authorization posture during the window to identify misconfiguration that permits anonymous access and confirm whether any requests returned successful responses that indicate real data exposure.
  • Hunt for follow-on activity from the same origin or user agent such as authenticated requests, service account token usage, RBAC/ClusterRoleBinding changes, pod exec, or secret/configmap reads to assess escalation beyond discovery.
  • If the API endpoint is publicly reachable, apply immediate containment by restricting network access to the API server (allowlisting, VPN/private endpoint, temporary IP blocks) while preserving relevant audit and network logs for forensics.

False positive analysis

  • Misconfigured or transitional API server authentication (e.g., anonymous auth briefly enabled or a failing authn proxy/fronting component) can cause legitimate clients to appear as system:anonymous and generate multiple 401/403/404 responses across several endpoints during normal cluster access attempts.
  • Internal cluster health checks or component discovery behavior that hits multiple API paths without presenting credentials (or uses requests that the audit log records with empty/null usernames) can resemble enumeration when it produces a short burst of failed requests across diverse resources from a single source IP and user agent.

Response and remediation

  • Immediately restrict Kubernetes API server network exposure by allowlisting known admin/VPN IPs and temporarily blocking the observed source IP(s) and user agent at the load balancer/firewall while preserving audit logs and reverse-proxy access logs for the timeframe.
  • Eradicate the anonymous access path by disabling anonymous authentication on the API server, fixing any misconfigured auth proxy that forwards unauthenticated traffic, and removing any RBAC bindings that grant permissions to system:anonymous or system:unauthenticated.
  • Validate whether any requests from the same source returned successful responses (especially reads of secrets/configmaps, tokenreviews/subjectaccessreviews, or CRDs) and, if so, rotate impacted service account tokens and credentials and perform a targeted review of recently issued tokens and new ClusterRoleBindings.
  • Recover by re-enabling API access in a controlled manner (private endpoint/VPN, bastion, or mTLS), confirming expected kubectl and controller functionality, and monitoring for renewed bursts of failed requests across many request URIs from unauthenticated identities.
  • Escalate to the incident response lead and platform security team if any anonymous request succeeded, if the probing repeats from multiple external IPs, or if follow-on activity appears (new privileged RBAC, pod exec, or secret reads) within 24 hours of the enumeration attempt.
  • Harden by enforcing least-privilege RBAC, enabling and retaining full audit logging for authn/authz failures, applying API server rate limits/WAF rules for repeated 401/403/404 sweeps, and continuously validating that the API endpoint is not publicly reachable.

References

Related rules

to-top