AWS EKS 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/08/03"
  3integration = ["aws"]
  4maturity = "production"
  5min_stack_comments = "The AWS integration adds the aws.eks_audit data stream in package 8.9.0, which requires Kibana 9.6.0 or later."
  6min_stack_version = "9.6.0"
  7updated_date = "2026/10/21"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12This rule detects potential endpoint enumeration attempts by an anonymous user. An anonymous user is a user that is not
 13authenticated or authorized to access the Kubernetes API server. By looking for a series of failed API requests, on
 14multiple endpoints, and a limited number of documents, this rule can detect automated permission enumeration attempts.
 15This behavior is uncommon for regular Kubernetes clusters.
 16"""
 17from = "now-6m"
 18language = "esql"
 19license = "Elastic License v2"
 20name = "AWS EKS Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected"
 21note = """## Triage and analysis
 22
 23> **Disclaimer**:
 24> 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.
 25
 26### Investigating AWS EKS Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected
 27This 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.
 28
 29### Possible investigation steps
 30
 31- 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.
 32- 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.
 33- 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.
 34- 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.
 35- 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.
 36
 37### False positive analysis
 38
 39- 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.
 40- 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.
 41
 42### Response and remediation
 43
 44- 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.
 45- 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`.
 46- 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.
 47- 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.
 48- 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.
 49- 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."""
 50references = ["https://heilancoos.github.io/research/2025/12/16/kubernetes.html#unauthenticated-api-access"]
 51risk_score = 47
 52rule_id = "a3dfb1f3-1613-5b16-b6cd-68edee3c54dd"
 53setup = """This rule reads EKS control-plane audit logs collected by the AWS integration
 54into `logs-aws.eks_audit-*`, where the AWS EKS audit ingest
 55pipelines normalize each audit record under `aws.eks.audit.*`.
 56
 57Enable the `audit` control-plane log type on the EKS cluster, send its
 58CloudWatch log group through the AWS integration, and confirm events carry
 59`data_stream.dataset: aws.eks_audit` and
 60`cloud.provider: aws` before enabling this rule.
 61"""
 62severity = "medium"
 63tags = [
 64    "Domain: Cloud",
 65    "Domain: Containers",
 66    "Platform: AWS",
 67    "Platform: Kubernetes",
 68    "Data Source: AWS",
 69    "Data Source: Amazon Web Services",
 70    "Data Source: Kubernetes API Server Audit Logs",
 71    "Service: AWS EKS",
 72    "Use Case: Threat Detection",
 73    "Tactic: Discovery",
 74    "Tactic: Reconnaissance",
 75    "Rule Type: ES|QL",
 76    "Resources: Investigation Guide",
 77    "Profile: Beta",
 78]
 79timestamp_override = "event.ingested"
 80type = "esql"
 81
 82query = '''
 83from logs-aws.eks_audit-* metadata _id, _index, _version
 84| where (
 85    aws.eks.audit.user.username in ("system:anonymous", "system:unauthenticated") or
 86    aws.eks.audit.impersonatedUser.username in ("system:anonymous", "system:unauthenticated") or
 87    aws.eks.audit.user.username is null or
 88    aws.eks.audit.user.username == ""
 89  ) and
 90  aws.eks.audit.level in ("RequestResponse", "ResponseComplete", "Request", "Metadata")
 91
 92| eval Esql.decision = `aws.eks.audit.annotations.authorization_k8s_io/decision`
 93| eval Esql.code = aws.eks.audit.responseStatus.code
 94
 95| eval Esql.outcome = case(
 96    Esql.decision == "allow", "authz_allow",
 97    Esql.decision == "forbid", "authz_forbid",
 98
 99    // fallback: infer from status when decision is missing
100    Esql.code in (401, 403), "authn_authz_failed",
101    (Esql.code >= 200 and Esql.code < 300), "success",
102    Esql.code == 404, "not_found",
103    Esql.code is null, "unknown",
104    true, "other_error"
105  )
106
107| stats
108    Esql.document_count = count(),
109
110    Esql.authz_allow_count = sum(case(Esql.outcome == "authz_allow", 1, 0)),
111    Esql.authz_forbid_count = sum(case(Esql.outcome == "authz_forbid", 1, 0)),
112
113    Esql.status_fail_count = sum(case(Esql.outcome == "authn_authz_failed", 1, 0)),
114    Esql.success_count = sum(case(Esql.outcome == "success", 1, 0)),
115    Esql.not_found_count = sum(case(Esql.outcome == "not_found", 1, 0)),
116    Esql.other_error_count = sum(case(Esql.outcome == "other_error", 1, 0)),
117    Esql.unknown_count = sum(case(Esql.outcome == "unknown", 1, 0)),
118
119    Esql.kubernetes_audit_verb_count_distinct = count_distinct(aws.eks.audit.verb),
120    Esql.kubernetes_audit_requestURI_count_distinct = count_distinct(aws.eks.audit.requestURI),
121    Esql.kubernetes_audit_objectRef_resource_count_distinct = count_distinct(aws.eks.audit.objectRef.resource),
122
123    Esql.kubernetes_audit_outcome_values = values(Esql.outcome),
124    Esql.kubernetes_audit_decision_values = values(Esql.decision),
125    Esql.kubernetes_audit_responseStatus_code_values = values(Esql.code),
126    Esql.kubernetes_audit_responseStatus_message_values = values(aws.eks.audit.responseStatus.message),
127
128    Esql.kubernetes_audit_verb_values = values(aws.eks.audit.verb),
129    Esql.kubernetes_audit_objectRef_resource_values = values(aws.eks.audit.objectRef.resource),
130    Esql.kubernetes_audit_objectRef_namespace_values = values(aws.eks.audit.objectRef.namespace),
131    Esql.kubernetes_audit_user_username_values = values(aws.eks.audit.user.username),
132    Esql.kubernetes_audit_user_groups_values = values(aws.eks.audit.user.groups),
133    Esql.kubernetes_audit_requestURI_values = values(aws.eks.audit.requestURI),
134    Esql.data_stream_namespace_values = values(data_stream.namespace)
135
136  BY aws.eks.audit.sourceIPs, user_agent.original
137
138| where
139    Esql.kubernetes_audit_requestURI_count_distinct > 5 and
140    Esql.kubernetes_audit_objectRef_resource_count_distinct > 3 and
141    Esql.document_count < 50 and
142    (Esql.authz_forbid_count >= 1 or Esql.status_fail_count >= 1 or Esql.not_found_count >= 3)
143
144| keep Esql.*, aws.eks.audit.sourceIPs, user_agent.original
145'''
146
147
148[[rule.threat]]
149framework = "MITRE ATT&CK"
150[[rule.threat.technique]]
151id = "T1613"
152name = "Container and Resource Discovery"
153reference = "https://attack.mitre.org/techniques/T1613/"
154
155
156[rule.threat.tactic]
157id = "TA0007"
158name = "Discovery"
159reference = "https://attack.mitre.org/tactics/TA0007/"
160[[rule.threat]]
161framework = "MITRE ATT&CK"
162[[rule.threat.technique]]
163id = "T1595"
164name = "Active Scanning"
165reference = "https://attack.mitre.org/techniques/T1595/"
166[[rule.threat.technique.subtechnique]]
167id = "T1595.003"
168name = "Wordlist Scanning"
169reference = "https://attack.mitre.org/techniques/T1595/003/"
170
171
172
173[rule.threat.tactic]
174id = "TA0043"
175name = "Reconnaissance"
176reference = "https://attack.mitre.org/tactics/TA0043/"

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 AWS EKS 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