Detection rules › Panther
GCP K8S Pod Create Or Modify Host Path Volume Mount
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.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | T1611 Escape to Host |
| Exfiltration | T1041 Exfiltration Over C2 Channel |
Rules detecting the same action
Other rules on this platform that filter on the same API call or operation.
Rule body yaml
AnalysisType: rule
RuleID: "GCP.K8S.Pod.Create.Or.Modify.Host.Path.Volume.Mount"
DisplayName: "GCP K8S Pod Create Or Modify Host Path Volume Mount"
Enabled: false
Status: Deprecated
LogTypes:
- GCP.AuditLog
Severity: High
Tags:
- Deprecated
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: |
Investigate the reason of adding hostPath volume mount. Advise that it is discouraged practice.
Create ticket if appropriate.
Reference: https://kubernetes.io/docs/concepts/security/pod-security-standards/#host-namespaces
Reports:
MITRE ATT&CK:
- TA0010:T1041 # Exfiltration Over C2 Channel
- TA0004:T1611 # Escape to Host
Filename: gcp_k8s_pod_create_or_modify_host_path_vol_mount.py
DedupPeriodMinutes: 360
Tests:
- Name: Pod With Suspicious Volume Mount Created
ExpectedResult: true
Log:
{
"logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
"protoPayload":
{
"at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": { "principalEmail": "some.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",
"imagePullPolicy": "Always",
"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",
"callerSuppliedUserAgent": "kubectl/v1.28.2 (darwin/amd64) kubernetes/89a4ea3",
},
"resourceName": "core/v1/namespaces/default/pods/test",
"response":
{
"spec":
{
"containers":
[
{
"image": "nginx",
"imagePullPolicy": "Always",
"name": "test",
"volumeMounts":
[{ "mountPath": "/test", "name": "test-volume" }],
},
],
"volumes":
[
{
"hostPath":
{
"path": "/var/lib/kubelet",
"type": "DirectoryOrCreate",
},
"name": "test-volume",
},
],
},
"status": { "phase": "Pending", "qosClass": "BestEffort" },
},
},
"receiveTimestamp": "2024-02-16 11:48:43.531373988",
"resource":
{
"labels":
{
"cluster_name": "some-project-cluster",
"location": "us-west1",
"project_id": "some-project",
},
"type": "k8s_cluster",
},
"timestamp": "2024-02-16 11:48:22.742154000",
}
- Name: Pod With Non-Suspicious Volume Mount Created
ExpectedResult: false
Log:
{
"logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
"protoPayload":
{
"at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": { "principalEmail": "some.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",
"imagePullPolicy": "Always",
"name": "test",
"volumeMounts":
[{ "mountPath": "/test", "name": "test-volume" }],
},
],
"volumes":
[
{
"hostPath":
{ "path": "/data", "type": "DirectoryOrCreate" },
"name": "test-volume",
},
],
},
},
"requestMetadata":
{
"callerIP": "1.2.3.4",
"callerSuppliedUserAgent": "kubectl/v1.28.2 (darwin/amd64) kubernetes/89a4ea3",
},
"resourceName": "core/v1/namespaces/default/pods/test",
"response":
{
"spec":
{
"containers":
[
{
"image": "nginx",
"imagePullPolicy": "Always",
"name": "test",
"volumeMounts":
[{ "mountPath": "/test", "name": "test-volume" }],
},
],
"volumes":
[
{
"hostPath":
{ "path": "/data", "type": "DirectoryOrCreate" },
"name": "test-volume",
},
],
},
"status": { "phase": "Pending", "qosClass": "BestEffort" },
},
},
"receiveTimestamp": "2024-02-16 11:48:43.531373988",
"resource":
{
"labels":
{
"cluster_name": "some-project-cluster",
"location": "us-west1",
"project_id": "some-project",
},
"type": "k8s_cluster",
},
"timestamp": "2024-02-16 11:48:22.742154000",
}
- Name: Pod Not Created
ExpectedResult: False
Log:
{
"logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity",
"protoPayload":
{
"at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": { "principalEmail": "some.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",
"imagePullPolicy": "Always",
"name": "test",
"volumeMounts":
[{ "mountPath": "/test", "name": "test-volume" }],
},
],
"volumes":
[
{
"hostPath":
{
"path": "/var/lib/kubelet",
"type": "DirectoryOrCreate",
},
"name": "test-volume",
},
],
},
"status": {},
},
"resourceName": "core/v1/namespaces/default/pods/test",
"response": { "status": "Failure" },
},
"receiveTimestamp": "2024-02-16 12:55:17.003485190",
"resource":
{
"labels":
{
"cluster_name": "some-project-cluster",
"location": "us-west1",
"project_id": "some-project",
},
"type": "k8s_cluster",
},
"timestamp": "2024-02-16 12:55:00.510160000",
}
Detection logic
Condition
not (protoPayload.response.status eq "Failure" or protoPayload.methodName not in ["io.k8s.core.v1.pods.create", "io.k8s.core.v1.pods.update", "io.k8s.core.v1.pods.patch"])
protoPayload.request.spec.volumes.hostPath.path is_not_null
protoPayload.request.spec.volumes.hostPath.path in ["/var/run/docker.sock", "/var/run/crio/crio.sock", "/var/lib/kubelet", "/var/lib/kubelet/pki", "/var/lib/docker/overlay2", "/etc/kubernetes", "/etc/kubernetes/manifests", "/etc/kubernetes/pki", "/home/admin"]
not ((protoPayload.authenticationInfo.principalEmail is_not_null and (protoPayload.authenticationInfo.principalEmail starts_with "system:kube-controller-manager" or protoPayload.authenticationInfo.principalEmail starts_with "system:kube-scheduler" or protoPayload.authenticationInfo.principalEmail starts_with "system:addon-manager" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:kube-system:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:kube-public:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:kube-node-lease:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:gke-system:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:gke-managed-system:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:gmp-system:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:gmp-public:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:config-management-system:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:istio-system:" or protoPayload.authenticationInfo.principalEmail starts_with "system:serviceaccount:asm-system:")) or protoPayload.resourceName is_not_null)
protoPayload.authorizationInfo is_not_null
protoPayload.authorizationInfo array_any
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.
| Field | Kind | Excluded values |
|---|---|---|
protoPayload.authenticationInfo.principalEmail | starts_with | system:addon-manager |
protoPayload.authenticationInfo.principalEmail | starts_with | system:kube-controller-manager |
protoPayload.authenticationInfo.principalEmail | starts_with | system:kube-scheduler |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:asm-system: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:config-management-system: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:gke-managed-system: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:gke-system: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:gmp-public: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:gmp-system: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:istio-system: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:kube-node-lease: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:kube-public: |
protoPayload.authenticationInfo.principalEmail | starts_with | system:serviceaccount:kube-system: |
protoPayload.authenticationInfo.principalEmail | is_not_null | |
protoPayload.resourceName | is_not_null | |
protoPayload.methodName | in | io.k8s.core.v1.pods.create, io.k8s.core.v1.pods.patch, io.k8s.core.v1.pods.update |
protoPayload.response.status | eq | Failure |
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.
| Field | Kind | Values |
|---|---|---|
protoPayload.authorizationInfo | is_not_null | |
protoPayload.methodName | in |
|
protoPayload.request.spec.volumes.hostPath.path | in |
|
protoPayload.request.spec.volumes.hostPath.path | is_not_null |
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.
| Field | Source |
|---|---|
project | resource.labels.project_id |
principal | protoPayload.authenticationInfo.principalEmail |
caller_ip | protoPayload.requestMetadata.callerIP |
methodName | protoPayload.methodName |
resourceName | protoPayload.resourceName |
serviceName | protoPayload.serviceName |