Detection rules › Panther

Kubernetes System Principal Accessed from Non-Cloud Public IP

Status
Experimental
Severity
high
Log types
Amazon.EKS.Audit, Azure.MonitorActivity, GCP.AuditLog
Tags
Kubernetes, Initial Access, Lateral Movement, Credential Access, Unified Detection
Reference
https://kubernetes.io/docs/concepts/security/rbac-good-practices/
Source
github.com/panther-labs/panther-analysis

This detection identifies when Kubernetes system principals (service accounts with usernames starting with "system:", "eks:", or "aks:") are accessed from non-cloud provider public IP addresses. System principals should only operate from within the cluster (private IPs) or from legitimate cloud infrastructure. Access from external public IPs indicates potential service account token theft or compromise, often following initial access to a cluster.

MITRE ATT&CK coverage

Rule body yaml

AnalysisType: rule
RuleID: "Kubernetes.System.Principal.PublicIP"
DisplayName: "Kubernetes System Principal Accessed from Non-Cloud Public IP"
Enabled: true
Status: Experimental
Filename: k8s_system_principal_public_ip.py
LogTypes:
  - Amazon.EKS.Audit
  - Azure.MonitorActivity
  - GCP.AuditLog
Tags:
  - Kubernetes
  - Initial Access
  - Lateral Movement
  - Credential Access
  - Unified Detection
Severity: High
Description: >
  This detection identifies when Kubernetes system principals (service accounts with usernames
  starting with "system:", "eks:", or "aks:") are accessed from non-cloud provider public IP
  addresses. System principals should only operate from within the cluster (private IPs) or
  from legitimate cloud infrastructure. Access from external public IPs indicates potential
  service account token theft or compromise, often following initial access to a cluster.
Runbook: |
  1. Find all Kubernetes API requests from the sourceIPs address in the 24 hours before and after the alert to identify targeted resources and operations
  2. Query for authentication and service account token events by this username in the 48 hours before the alert to determine if the token was recently compromised
  3. Search for other system principal alerts from the same sourceIPs address across all clusters in the past 7 days to assess campaign scope
Reference: https://kubernetes.io/docs/concepts/security/rbac-good-practices/
Reports:
  MITRE ATT&CK:
    - TA0001:T1190 # Initial Access: Exploit Public-Facing Application
    - TA0006:T1528 # Credential Access: Steal Application Access Token
    - TA0008:T1021.007 # Lateral Movement: Remote Services: Cloud Services
DedupPeriodMinutes: 60
SummaryAttributes:
  - username
  - p_any_ip_addresses
  - p_source_label
Tests:
  - Name: EKS System ServiceAccount from Non-AWS Public IP
    ExpectedResult: true
    Log:
      {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "verb": "get",
        "user": {
          "username": "system:serviceaccount:kube-system:coredns",
          "groups": ["system:serviceaccounts", "system:authenticated"]
        },
        "sourceIPs": ["1.2.3.4"],
        "objectRef": {
          "resource": "endpointslices",
          "apiVersion": "v1"
        },
        "responseStatus": {"code": 200},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit",
        "p_source_label": "eks-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "sourceIPs": [{
              "asn": "AS12345",
              "domain": "example-isp.com",
              "name": "Example ISP",
              "p_match": "1.2.3.4",
              "type": "isp"
            }]
          }
        }
      }
  - Name: AKS System ServiceAccount from Non-Azure Public IP
    ExpectedResult: true
    Log:
      {
        "p_log_type": "Azure.MonitorActivity",
        "category": "kube-audit",
        "operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
        "properties": {
          "log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"list\",\"user\":{\"username\":\"system:serviceaccount:kube-system:metrics-server\",\"groups\":[\"system:serviceaccounts\",\"system:authenticated\"]},\"sourceIPs\":[\"8.8.8.8\"],\"objectRef\":{\"resource\":\"nodes\"},\"responseStatus\":{\"code\":200},\"stage\":\"ResponseComplete\"}"
        },
        "p_source_label": "aks-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "sourceIPs": [{
              "asn": "AS15169",
              "domain": "google.com",
              "name": "Google LLC",
              "p_match": "8.8.8.8",
              "type": "hosting"
            }]
          }
        }
      }
  - Name: GKE System ServiceAccount from Non-GCP Public IP
    ExpectedResult: true
    Log:
      {
        "protoPayload": {
          "authenticationInfo": {"principalEmail": "system:serviceaccount:kube-system:default"},
          "methodName": "io.k8s.core.v1.pods.list",
          "requestMetadata": {
            "callerIP": "1.2.3.4",
            "callerSuppliedUserAgent": "kubectl/v1.27.0"
          },
          "resourceName": "core/v1/namespaces/default/pods",
          "serviceName": "k8s.io",
          "status": {}
        },
        "resource": {
          "type": "k8s_cluster",
          "labels": {"project_id": "test-project"}
        },
        "p_log_type": "GCP.AuditLog",
        "p_source_label": "gke-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "callerIP": [{
              "asn": "AS12345",
              "domain": "example-isp.com",
              "name": "Example ISP",
              "p_match": "1.2.3.4",
              "type": "isp"
            }]
          }
        }
      }
  - Name: EKS Legitimate Node from AWS IP
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "apiVersion": "audit.k8s.io/v1",
        "verb": "create",
        "user": {
          "username": "system:node:ip-192-168-3-178.us-west-2.compute.internal",
          "groups": ["system:nodes", "system:authenticated"]
        },
        "sourceIPs": ["54.212.83.236"],
        "objectRef": {
          "resource": "serviceaccounts",
          "subresource": "token"
        },
        "responseStatus": {"code": 201},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit",
        "p_source_label": "eks-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "sourceIPs": [{
              "asn": "AS16509",
              "domain": "amazon.com",
              "name": "Amazon.com, Inc.",
              "p_match": "54.212.83.236",
              "route": "54.212.0.0/16",
              "type": "hosting"
            }]
          }
        }
      }
  - Name: EKS addon-manager from AWS Lambda
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "get",
        "user": {
          "username": "eks:addon-manager",
          "extra": {
            "arn": ["arn:aws:sts::123412341234:assumed-role/AWSWesleyClusterManagerLambda-Add-AddonManagerRole/session"]
          },
          "groups": ["system:authenticated"]
        },
        "sourceIPs": ["35.163.244.48"],
        "objectRef": {"resource": "deployments", "namespace": "kube-system"},
        "responseStatus": {"code": 200},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: Non-System User from Public IP
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "get",
        "user": {"username": "admin@example.com"},
        "sourceIPs": ["1.2.3.4"],
        "objectRef": {"resource": "pods"},
        "responseStatus": {"code": 200},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: System User from Private IP
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "watch",
        "user": {"username": "system:serviceaccount:kube-system:coredns"},
        "sourceIPs": ["10.0.27.115"],
        "objectRef": {"resource": "endpointslices"},
        "responseStatus": {"code": 200},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: 403 Response (Excluded)
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "get",
        "user": {"username": "system:serviceaccount:default:test"},
        "sourceIPs": ["1.2.3.4"],
        "objectRef": {"resource": "secrets"},
        "responseStatus": {"code": 403},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: AKS CSI Disk Driver from Azure Node
    ExpectedResult: false
    Log:
      {
        "p_log_type": "Azure.MonitorActivity",
        "category": "kube-audit",
        "operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
        "properties": {
          "log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"get\",\"user\":{\"username\":\"system:serviceaccount:kube-system:csi-azuredisk-node-sa\",\"groups\":[\"system:serviceaccounts\",\"system:authenticated\"]},\"sourceIPs\":[\"20.127.0.45\"],\"objectRef\":{\"resource\":\"nodes\"},\"responseStatus\":{\"code\":200},\"stage\":\"ResponseComplete\"}"
        },
        "p_source_label": "aks-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "sourceIPs": [{
              "asn": "AS8075",
              "domain": "microsoft.com",
              "name": "Microsoft Corporation",
              "p_match": "20.127.0.45",
              "type": "hosting"
            }]
          }
        }
      }
  - Name: AKS CSI File Driver from Azure Node
    ExpectedResult: false
    Log:
      {
        "p_log_type": "Azure.MonitorActivity",
        "category": "kube-audit",
        "operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
        "properties": {
          "log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"list\",\"user\":{\"username\":\"system:serviceaccount:kube-system:csi-azurefile-node-sa\",\"groups\":[\"system:serviceaccounts\",\"system:authenticated\"]},\"sourceIPs\":[\"52.152.245.238\"],\"objectRef\":{\"resource\":\"persistentvolumes\"},\"responseStatus\":{\"code\":200},\"stage\":\"ResponseComplete\"}"
        },
        "p_source_label": "aks-cluster"
      }
  - Name: GKE Anonymous Health Check with User Agent
    ExpectedResult: false
    Log:
      {
        "protoPayload": {
          "authenticationInfo": {"principalEmail": "system:anonymous"},
          "methodName": "io.k8s.core.v1.healthz.get",
          "requestMetadata": {
            "callerIP": "35.186.224.25",
            "callerSuppliedUserAgent": "GoogleHC/1.0"
          },
          "resourceName": "core/v1/healthz",
          "serviceName": "k8s.io",
          "status": {}
        },
        "resource": {
          "type": "k8s_cluster",
          "labels": {"project_id": "test-project"}
        },
        "p_log_type": "GCP.AuditLog",
        "p_source_label": "gke-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "callerIP": [{
              "asn": "AS15169",
              "domain": "google.com",
              "name": "Google LLC",
              "p_match": "35.186.224.25",
              "type": "hosting"
            }]
          }
        }
      }
  - Name: GKE Anonymous Readyz Check via RequestURI
    ExpectedResult: false
    Log:
      {
        "protoPayload": {
          "authenticationInfo": {"principalEmail": "system:anonymous"},
          "methodName": "io.k8s.readyz",
          "requestMetadata": {
            "callerIP": "108.177.75.65",
            "callerSuppliedUserAgent": "GoogleKubernetesEngineFrontend"
          },
          "serviceName": "k8s.io",
          "status": {}
        },
        "p_log_type": "GCP.AuditLog",
        "p_source_label": "gke-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "callerIP": [{
              "asn": "AS15169",
              "domain": "google.com"
            }]
          }
        }
      }
  - Name: Pod with Mixed Public and Private IPs
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "watch",
        "user": {"username": "system:serviceaccount:default:app"},
        "sourceIPs": ["10.0.1.5", "54.212.83.236"],
        "objectRef": {"resource": "endpoints"},
        "responseStatus": {"code": 200},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: AKS System Node from Azure IP
    ExpectedResult: false
    Log:
      {
        "p_log_type": "Azure.MonitorActivity",
        "category": "kube-audit",
        "operationName": "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read",
        "properties": {
          "log": "{\"kind\":\"Event\",\"apiVersion\":\"audit.k8s.io/v1\",\"verb\":\"create\",\"user\":{\"username\":\"system:node:aks-nodepool1-12345-vmss000001\",\"groups\":[\"system:nodes\",\"system:authenticated\"]},\"sourceIPs\":[\"20.127.0.100\"],\"objectRef\":{\"resource\":\"leases\"},\"responseStatus\":{\"code\":201},\"stage\":\"ResponseComplete\"}"
        },
        "p_source_label": "aks-cluster",
        "p_enrichment": {
          "ipinfo_asn": {
            "sourceIPs": [{
              "asn": "AS8075",
              "domain": "microsoft.com",
              "name": "Microsoft Corporation",
              "p_match": "20.127.0.100",
              "type": "hosting"
            }]
          }
        }
      }
  - Name: Invalid IP in sourceIPs (Skip Invalid)
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "get",
        "user": {"username": "system:serviceaccount:kube-system:coredns"},
        "sourceIPs": ["not-an-ip", "10.0.1.5"],
        "objectRef": {"resource": "services"},
        "responseStatus": {"code": 200},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: system:anonymous from Public IP (Excluded - Not a Real Principal)
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "propfind",
        "user": {"username": "system:anonymous"},
        "sourceIPs": ["176.65.134.20"],
        "objectRef": {"resource": ""},
        "responseStatus": {"code": 403},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: system:unauthenticated from Public IP (Excluded - Not a Real Principal)
    ExpectedResult: false
    Log:
      {
        "kind": "Event",
        "verb": "get",
        "user": {"username": "system:unauthenticated"},
        "sourceIPs": ["1.2.3.4"],
        "objectRef": {"resource": "apis"},
        "responseStatus": {"code": 403},
        "stage": "ResponseComplete",
        "p_log_type": "Amazon.EKS.Audit"
      }
  - Name: GKE system:anonymous Health Check (Excluded - Anonymous)
    ExpectedResult: false
    Log:
      {
        "protoPayload": {
          "authenticationInfo": {"principalEmail": "system:anonymous"},
          "methodName": "io.k8s.readyz",
          "requestMetadata": {
            "callerIP": "108.177.75.65",
            "callerSuppliedUserAgent": "GoogleHC/1.0"
          },
          "resourceName": "/readyz",
          "serviceName": "k8s.io",
          "status": {}
        },
        "p_log_type": "GCP.AuditLog",
        "p_source_label": "gke-cluster"
      }

Detection logic

Condition

not (stage is_not_null and stage ne "ResponseComplete")
responseStatus.code ne "403"
username starts_with "system:serviceaccount:" or username starts_with "system:node:" or username starts_with "eks:" or username starts_with "aks:"
sourceIPs is_not_null
not ((p_log_type contains "Amazon.EKS" and (username in ["eks:addon-manager", "eks:node-manager"] or (username starts_with "system:node:" and user.groups contains "system:nodes" and user.groups contains "system:authenticated"))) or (p_log_type contains "Azure.MonitorActivity" and (username in ["system:serviceaccount:kube-system:csi-azuredisk-node-sa", "system:serviceaccount:kube-system:csi-azurefile-node-sa", "system:serviceaccount:kube-system:csi-secrets-store-provider-azure", "system:serviceaccount:kube-system:cloud-node-manager"] or username starts_with "system:node:")) or (p_log_type contains "GCP.AuditLog" and (username starts_with "system:node:" or (username eq "system:anonymous" and protoPayload.requestMetadata.callerSuppliedUserAgent in ["GoogleKubernetesEngineFrontend", "GoogleHC/1.0"] and (protoPayload.resourceName ends_with "readyz" or requestURI ends_with "readyz" or protoPayload.resourceName ends_with "livez" or requestURI ends_with "livez" or protoPayload.resourceName ends_with "healthz" or requestURI ends_with "healthz")))))

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

Exclusions

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

FieldKindExcluded values
protoPayload.resourceNameends_withhealthz
protoPayload.resourceNameends_withlivez
protoPayload.resourceNameends_withreadyz
requestURIends_withhealthz
requestURIends_withlivez
requestURIends_withreadyz
protoPayload.requestMetadata.callerSuppliedUserAgentinGoogleHC/1.0, GoogleKubernetesEngineFrontend
usernameeqsystem:anonymous
usernamestarts_withsystem:node:
p_log_typecontainsGCP.AuditLog
user.groupscontainssystem:authenticated
user.groupscontainssystem:nodes
usernamestarts_withsystem:node:
usernameineks:addon-manager, eks:node-manager
p_log_typecontainsAmazon.EKS
usernamestarts_withsystem:node:
usernameinsystem:serviceaccount:kube-system:cloud-node-manager, system:serviceaccount:kube-system:csi-azuredisk-node-sa, system:serviceaccount:kube-system:csi-azurefile-node-sa, system:serviceaccount:kube-system:csi-secrets-store-provider-azure
p_log_typecontainsAzure.MonitorActivity
stageis_not_null(no value, null check)
stageneResponseComplete

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
responseStatus.codene
  • 403
sourceIPsis_not_null
  • (no value, null check)
usernamestarts_with
  • aks:
  • eks:
  • system:node:
  • system:serviceaccount:

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
username
sourceIPs
userAgent
namespace
verb
resource
requestURI
responseStatus
clusterp_source_label