AnalysisType: rule
RuleID: "Kubernetes.Pod.HostPath.Volume"
DisplayName: "Kubernetes Pod With HostPath Volume Mount"
Enabled: true
Filename: k8s_pod_hostpath_volume.py
LogTypes:
- Amazon.EKS.Audit
- Azure.MonitorActivity
- GCP.AuditLog
Severity: Medium
Description: >
This detection monitors for pod creation with a hostPath volume mount. The attachment to a
node's volume can allow for privilege escalation through underlying vulnerabilities or it can
open up possibilities for data exfiltration or unauthorized file access. It is very rare to see
this being a pod requirement. System service accounts in the kube-system namespace are excluded
to prevent false positives from legitimate system components.
Runbook: |
1. Review all pod creation events by the username in the 24 hours before the alert to establish baseline deployment behavior
2. Check if the hostPath volume is mounted from sensitive paths (/, /var, /sys, /proc, /etc) which pose higher security risk
3. Search for other hostPath volume mounts by this user in the past 30 days and compare the paths being accessed to identify patterns
Reference: >
- https://kubernetes.io/docs/concepts/security/pod-security-standards/#host-namespaces
- https://medium.com/@vincn.ledan/understanding-the-risks-injecting-malicious-pods-via-hostpath-in-kubernetes-due-to-83f54a1bef31
Reports:
Stratus Red Team:
- k8s.privilege-escalation.hostpath-volume
MITRE ATT&CK:
- TA0010:T1041 # Exfiltration Over C2 Channel
- TA0004:T1611 # Escape to Host
DedupPeriodMinutes: 360
Tags:
- Kubernetes
- Security Control
- Privilege Escalation
- Data Exfiltration
- Unified Detection
Tests:
- Name: EKS Pod With Suspicious HostPath
ExpectedResult: true
Log:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"auditID": "abc-123",
"verb": "create",
"user": {"username": "user@company.com"},
"sourceIPs": ["1.2.3.4"],
"userAgent": "kubectl/v1.28.2",
"objectRef": {
"resource": "pods",
"namespace": "default",
"name": "test",
"apiVersion": "v1"
},
"responseStatus": {"code": 201},
"requestObject": {
"kind": "Pod",
"apiVersion": "v1",
"metadata": {"name": "test", "namespace": "default"},
"spec": {
"containers": [{
"name": "test",
"image": "nginx",
"volumeMounts": [{"mountPath": "/test", "name": "test-volume"}]
}],
"volumes": [{
"name": "test-volume",
"hostPath": {
"path": "/var/lib/kubelet",
"type": "DirectoryOrCreate"
}
}]
}
},
"p_log_type": "Amazon.EKS.Audit",
"p_source_label": "eks-cluster"
}
- Name: AKS Pod With HostPath
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\":\"create\",\"user\":{\"username\":\"admin@example.com\"},\"sourceIPs\":[\"10.0.0.1\"],\"objectRef\":{\"resource\":\"pods\",\"namespace\":\"default\",\"name\":\"test\"},\"responseStatus\":{\"code\":201},\"requestObject\":{\"kind\":\"Pod\",\"spec\":{\"containers\":[{\"name\":\"nginx\",\"volumeMounts\":[{\"mountPath\":\"/data\",\"name\":\"vol\"}]}],\"volumes\":[{\"name\":\"vol\",\"hostPath\":{\"path\":\"/mnt/data\"}}]}}}"
},
"p_source_label": "aks-cluster"
}
- Name: GCP GKE Pod With HostPath
ExpectedResult: true
Log:
{
"logName": "projects/test-project/logs/cloudaudit.googleapis.com%2Factivity",
"protoPayload": {
"authenticationInfo": {"principalEmail": "user@company.com"},
"authorizationInfo": [{
"granted": true,
"permission": "io.k8s.core.v1.pods.create",
"resource": "core/v1/namespaces/default/pods/test"
}],
"methodName": "io.k8s.core.v1.pods.create",
"request": {
"@type": "core.k8s.io/v1.Pod",
"apiVersion": "v1",
"kind": "Pod",
"metadata": {"name": "test", "namespace": "default"},
"spec": {
"containers": [{
"image": "nginx",
"name": "test",
"volumeMounts": [{"mountPath": "/test", "name": "test-volume"}]
}],
"volumes": [{
"hostPath": {
"path": "/var/lib/kubelet",
"type": "DirectoryOrCreate"
},
"name": "test-volume"
}]
}
},
"requestMetadata": {"callerIP": "1.2.3.4"},
"resourceName": "core/v1/namespaces/default/pods/test",
"serviceName": "k8s.io"
},
"resource": {
"labels": {
"cluster_name": "gke-cluster",
"project_id": "test-project"
},
"type": "k8s_cluster"
},
"p_log_type": "GCP.AuditLog",
"p_source_label": "gke-cluster",
"timestamp": "2024-02-16 11:48:22.742"
}
- Name: Pod Without HostPath
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "create",
"objectRef": {"resource": "pods", "namespace": "default"},
"responseStatus": {"code": 201},
"requestObject": {
"spec": {
"containers": [{"name": "nginx"}],
"volumes": [{
"name": "config",
"configMap": {"name": "app-config"}
}]
}
},
"p_log_type": "Amazon.EKS.Audit"
}
- Name: Pod Creation Failed
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "create",
"objectRef": {"resource": "pods", "namespace": "default"},
"responseStatus": {"code": 403, "status": "Failure"},
"requestObject": {
"spec": {
"volumes": [{
"hostPath": {"path": "/var/lib/kubelet"}
}]
}
},
"p_log_type": "Amazon.EKS.Audit"
}
- Name: System Namespace Pod (Excluded)
ExpectedResult: false
Log:
{
"kind": "Event",
"verb": "create",
"user": {"username": "system:serviceaccount:kube-system:controller"},
"objectRef": {
"resource": "pods",
"namespace": "kube-system",
"name": "network-agent"
},
"responseStatus": {"code": 201},
"requestObject": {
"spec": {
"volumes": [{
"hostPath": {"path": "/var/lib/cni"}
}]
}
},
"p_log_type": "Amazon.EKS.Audit"
}
# ------ paired body: k8s_pod_hostpath_volume.py ------
from panther_kubernetes_helpers import (
get_hostpath_paths,
get_pod_context_fields,
get_pod_name,
has_hostpath_volume,
is_failed_request,
is_sensitive_hostpath,
is_system_namespace,
is_system_principal,
k8s_alert_context,
)
def rule(event):
verb = event.udm("verb")
resource = event.udm("resource")
namespace = event.udm("namespace")
username = event.udm("username")
response_status = event.udm("responseStatus")
# Only check pod creation events
if verb != "create" or resource != "pods":
return False
# Skip failed requests
if is_failed_request(response_status):
return False
# Exclude system principals creating pods in system namespaces (legitimate)
# but alert on system principals in user namespaces (malicious Deployments)
# and alert on user-created pods in system namespaces (suspicious)
if is_system_principal(username) and is_system_namespace(namespace):
return False
# Check for hostPath volumes
volumes = event.udm("volumes") or []
if has_hostpath_volume(volumes):
return True
return False
def severity(event):
volumes = event.udm("volumes") or []
paths = get_hostpath_paths(volumes)
for path in paths:
if is_sensitive_hostpath(path):
return "HIGH"
return "DEFAULT"
def title(event):
username = event.udm("username") or "<UNKNOWN_USER>"
namespace = event.udm("namespace") or "<UNKNOWN_NAMESPACE>"
name = get_pod_name(event)
volumes = event.udm("volumes") or []
paths = get_hostpath_paths(volumes)
paths_str = ", ".join(paths) if paths else "unknown"
return (
f"[{username}] created pod [{namespace}/{name}] with hostPath volume mount "
f"[{paths_str}]"
)
def dedup(event):
username = event.udm("username") or "<UNKNOWN_USER>"
namespace = event.udm("namespace") or "<UNKNOWN_NAMESPACE>"
return f"hostpath_volume_{username}_{namespace}"
def alert_context(event):
volumes = event.udm("volumes") or []
pod_context = get_pod_context_fields(event)
return k8s_alert_context(
event,
extra_fields={
**pod_context,
"hostpath_paths": get_hostpath_paths(volumes),
},
)