Detection rules › Panther

Panther rules: anonymous

EKS Anonymous API Access Detected

#
Severity
low
Group by
userAgent
Entities
aws_account_ids, aws_arns, ip_addresses, usernames
Log types
Amazon.EKS.Audit
Tags
EKS, Security Control, API, Initial Access:Exploit Public-Facing Application
Reference
raesene.github.io
Source
github.com/panther-labs/panther-analysis

This rule detects anonymous API requests made to the Kubernetes API server. In production environments, anonymous access should be disabled to prevent unauthorized access to the API server.

MITRE ATT&CK coverage

TacticTechniques
Initial Access

Detection logic

from ipaddress import ip_address

from panther_aws_helpers import eks_panther_obj_ref


def rule(event):
    src_ip = event.get("sourceIPs", ["0.0.0.0"])  # nosec
    if src_ip == ["127.0.0.1"]:
        return False
    if event.get("userAgent", "") == "ELB-HealthChecker/2.0":
        try:
            if ip_address(src_ip[0]).is_private:
                return False
        except ValueError:
            pass

    # Check if the username is set to "system:anonymous", which indicates anonymous access
    if event.deep_get("user", "username") == "system:anonymous":
        return True
    return False


def title(event):
    # For INFO-level events, just group them all together since they're not that interesting
    if event.deep_get("annotations", "authorization.k8s.io/decision") != "allow":
        return "Failed Anonymous EKS Access Attempt(s) Detected"
    if event.get("requestURI") == "/version":
        return "Anonymous EKS Access to /version Endpoint Detected"
    p_eks = eks_panther_obj_ref(event)
    return (
        f"Anonymous API access detected on Kubernetes API server "
        f"from [{p_eks.get('sourceIPs')[0]}] to [{event.get('requestURI', 'NO_URI')}] "
        f"on [{p_eks.get('p_source_label')}]"
    )


def severity(event):
    if event.deep_get("annotations", "authorization.k8s.io/decision") != "allow":
        return "INFO"
    if event.get("requestURI") == "/version":
        return "INFO"
    return "DEFAULT"


def dedup(event):
    # For INFO-level events, just group them all together since they're not that interesting
    if severity(event) == "INFO":
        return "no dedup"
    p_eks = eks_panther_obj_ref(event)
    return f"anonymous_access_{p_eks.get('p_source_label')}_{event.get('userAgent')}"


def alert_context(event):
    p_eks = eks_panther_obj_ref(event)
    mutable_event = event.to_dict()
    mutable_event["p_eks"] = p_eks
    return dict(mutable_event)

Rule specification

AnalysisType: rule
Filename: anonymous_api_access.py
RuleID: "Amazon.EKS.AnonymousAPIAccess"
DisplayName: "EKS Anonymous API Access Detected"
Enabled: true
LogTypes:
  - Amazon.EKS.Audit
Severity: Low
Reports:
  MITRE ATT&CK:
    - "TA0001:T1190" # Initial Access: Exploit Public-Facing Application
Description: >
  This rule detects anonymous API requests made to the Kubernetes API server.
  In production environments, anonymous access should be disabled to prevent
  unauthorized access to the API server.
DedupPeriodMinutes: 60
Reference: 
  https://raesene.github.io/blog/2023/03/18/lets-talk-about-anonymous-access-to-Kubernetes/
Runbook: |
  Check the EKS cluster configuration and ensure that anonymous access
  to the Kubernetes API server is disabled. This can be done by verifying the  API
  server arguments and authentication webhook configuration.
SummaryAttributes:
  - user:username
  - p_any_ip_addresses
  - p_source_label
Tags:
  - EKS
  - Security Control
  - API
  - Initial Access:Exploit Public-Facing Application

Stages and Predicates

Fires on Amazon.EKS.Audit events when the condition below holds.

Condition

  • user.username is system:anonymous

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
requestURI

Response runbook

Check the EKS cluster configuration and ensure that anonymous access

to the Kubernetes API server is disabled. This can be done by verifying the API

server arguments and authentication webhook configuration.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "annotations": {
    "authorization.k8s.io/decision": "allow",
    "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding system:public-info-viewer"
  },
  "apiVersion": "audit.k8s.io/v1",
  "auditID": "abcde12345",
  "kind": "Event",
  "level": "Request",
  "objectRef": {
    "apiVersion": "v1",
    "name": "test-pod",
    "namespace": "default",
    "resource": "pods"
  },
  "p_any_aws_account_ids": [
    "123412341234"
  ],
  "p_any_aws_arns": [
    "arn:aws:iam::123412341234:role/DevAdministrator"
  ],
  "p_any_ip_addresses": [
    "8.8.8.8"
  ],
  "p_any_usernames": [
    "system:anonymous"
  ],
  "p_event_time": "2022-11-29 00:09:04.38",
  "p_log_type": "Amazon.EKS.Audit",
  "p_parse_time": "2022-11-29 00:10:25.067",
  "p_row_id": "2e4ab474b0f0f7a4a8fff4f014a9b32a",
  "p_source_id": "4c859cd4-9406-469b-9e0e-c2dc1bee24fa",
  "p_source_label": "example-cluster-eks-logs",
  "requestReceivedTimestamp": "2022-11-29 00:09:04.38",
  "requestURI": "/api/v1/namespaces/default/pods/test-pod",
  "responseStatus": {
    "code": 200
  },
  "sourceIPs": [
    "8.8.8.8"
  ],
  "stage": "ResponseComplete",
  "user": {
    "username": "system:anonymous"
  },
  "userAgent": "kubectl/v1.25.4"
}