Kubernetes Service Account Abuse and Escape
An intruder has harvested a service account token from a compromised pod and is using it to deploy a privileged pod for container escape, bypassing standard runtime process detection.
Based on research by Elastic Security Labs 2026-09-20 11 steps · 4 queries T1552.006 T1609 T1610 T1611 T1613
Brief
Why this hunt
Elastic Security Labs published a deep dive titled How to correlate Kubernetes audit logs with container runtime data (https://www.elastic.co/security-labs/threat-command/kubernetes-audit-logs-container-escape). The research highlights the difficulty of tracking an adversary as they move from a single container to the underlying host. We designed this hunt to bridge that gap by connecting runtime process events with the Kubernetes control plane.
How the Hunt Flows
The first phase focuses on scoping and identifying the environment. We use the hb_software_inventory surface to find hosts running Kubernetes components like kubelet, docker, or containerd. This step ensures the hunt only runs on relevant infrastructure and reduces the processing load on unrelated systems.
Once we identify the targets, we look for evidence of discovery and harvesting. One branch of the hunt queries hb_process_activity for rare executions of tools like kubectl, kube-hunter, or kube-bench. We specifically look for manual commands like getting pods or secrets. At the same time, we check hb_file_activity for any process reading the service account token from the standard secret path.
The triage phase then links these two activities. An analyst looks for the same host or process context where discovery tools were run and tokens were accessed. This connection suggests a purposeful attempt to gather credentials for a lateral move toward the cluster API.
The next phase examines the Kubernetes control plane. We query the kubernetes_pod surface for pods configured with host_pid, host_network, or host_ipc set to true. These settings are the hallmarks of a container escape attempt. By correlating the timing of these deployments with the earlier token harvesting, the hunt confirms the intent and impact of the attack.
The final assessment weighs the early-stage discovery findings against the appearance of these privileged pods. Does the timing and identity suggest the discovery led directly to this deployment? An analyst confirms the verdict and initiates isolation if the attack chain is complete.
Blind Spots and Limitations
This hunt has two primary blind spots. First, it cannot see the specific commands executed via kubectl exec because that data resides in Kubernetes Audit Logs, which are not currently part of the queried hb_ surfaces. Second, the hunt relies on periodic snapshots of the pod inventory. If an adversary deploys an ephemeral privileged pod that only exists for a few seconds to perform an escape, the inventory might miss it.
Running the Hunt
This hunt is an open hunt.md playbook. You can import it into Huntbase or any runtime that supports the hunt.md format. It provides the necessary queries and logic to automate the correlation between endpoint telemetry and container orchestration layers.
Steps
-
Identify Kubernetes infrastructure
Query · scopingLocate hosts running Kubernetes or container runtimes to focus the hunt, filtering out unrelated system packages.
reads hb_software_inventorysqlSELECT DISTINCT device_hostname FROM hb_software_inventory WHERE (LOWER(package_name) LIKE '%kube%' OR LOWER(package_name) LIKE '%docker%' OR LOWER(package_name) LIKE '%containerd%') AND (package_type = 'deb' OR package_type = 'rpm') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)What a hit looks like. A list of hostnames hosting Kubernetes components. Silence means no K8s-related system packages are installed.
-
Discovery tool prevalence
Query · baselineFind evidence of cluster enumeration using common tools, checking for rare executions that indicate manual intervention.
reads hb_process_activitysqlSELECT process_name, process_cmd_line, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_process_activity WHERE (LOWER(process_name) LIKE '%/kubectl' OR LOWER(process_name) LIKE '%/kube-hunter' OR LOWER(process_name) LIKE '%/kube-bench' OR instr(',' || '{{discovery_commands}}' || ',', ',' || LOWER(process_name) || ',') > 0 OR LOWER(process_cmd_line) LIKE '%get pods%' OR LOWER(process_cmd_line) LIKE '%get secrets%') AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) GROUP BY process_name, process_cmd_line HAVING hosts <= 5 ORDER BY hostsWhat a hit looks like. A rare process execution indicating manual discovery. Silence suggests no common tools were run via monitored process events.
-
Service account token harvesting
Query · detection candidateDetect access to Kubernetes service account tokens, which are the target for attackers seeking to abuse the API.
reads hb_file_activitysqlSELECT device_hostname, process_name, process_cmd_line, file_path, time FROM hb_file_activity WHERE instr(',' || '{{k8s_secret_paths}}' || ',', ',' || LOWER(file_path) || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)What a hit looks like. Processes reading tokens from /var/run/secrets. Access from non-system processes is a high-fidelity indicator.
-
Triage early-stage activity
Agent triageAssess whether discovery and token access are linked through the same host and process context.
-
Identify privileged pod deployments
Query · triageFind pods with high-risk configurations that facilitate container escape by sharing host namespaces.
reads kubernetes_podsqlSELECT hostname, host_pid, host_network, host_ipc, creation_timestamp, context_name FROM kubernetes_pod WHERE (host_pid = 1 OR host_network = 1 OR host_ipc = 1) AND creation_timestamp >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Pods created with host namespace permissions. Silence means no pods with these dangerous settings were detected in the current inventory.
-
Final intrusion assessment
Agent triageCorrelate the early beachhead with the deployment of privileged infrastructure to confirm a complete attack chain.
-
Route on verdict
DecisionDecide whether to isolate affected assets based on confirmed escape indicators.
-
Isolate host and revoke SA
Response actionStop the intruder from using host namespaces or further abusing the API.
-
Analyst review
Analyst taskVerify the attack path and ensure no persistent access remains.
-
Close out
Analyst taskDocument results and tune detection candidates.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Service account discovery T1613 |
Yes | discovery-tool-prevalence |
| Service account token harvesting T1552.006 |
Yes | token-harvesting-activity |
| Privileged pod deployment T1610 |
Yes | privileged-pod-deployment |
| Interactive pod shell T1609 |
Not visible | Requires Kubernetes audit logs to see decoded requestURIs for exec commands; not present in available hb_ surfaces. |
| Container escape via host utilities T1611 |
Not visible | Escape wrappers nsenter/chroot in URI format require API audit logs; hb_process_activity may see them if run as host processes, but the API interaction is invisible. |
Blind spots
- Needs Kubernetes Audit Logs (ResponseComplete stage). Attackers can perform interaction and escape without leaving a trace in standard runtime process events if they use API-based methods. It would answer What commands were executed via 'kubectl exec'?. Remediation: Enable and ingest Kubernetes API audit logs with request detail.
- Needs Continuous pod inventory (live stream). Snapshot-based inventory like kubernetes_pod may miss short-lived attack pods used for one-off commands. It would answer Did a pod exist only for a few seconds during the escape attempt?. Remediation: Move from periodic inventory snapshots to an event-driven pod creation stream.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
discovery_commands | list[string] | kubectl, kube-hunter, kube-bench, microk8s, k3s | Common Kubernetes discovery tools and command fragments. |
k8s_secret_paths | list[path] | /var/run/secrets/kubernetes.io/serviceaccount/token, /var/run/secrets/kubernetes.io/serviceaccount/ca.crt | Paths to Kubernetes service account secrets. |
lookback_days | number | 14 | Days of history to examine. |
scope_hosts | list[host] | — | Specific hosts to narrow the hunt; leave empty for fleet-wide. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
Source
---
analysis: A single rule on privileged pods triggers high noise from system pods; a
rule on token access misses the context. This hunt joins the 'why' (discovery) and
'how' (token harvest) with 'impact' (privileged deployment) to create a high-fidelity
attack chain.
blind_spots:
- id: missing-k8s-audit-logs
question: What commands were executed via 'kubectl exec'?
remediation: Enable and ingest Kubernetes API audit logs with request detail.
requires: Kubernetes Audit Logs (ResponseComplete stage)
risk: Attackers can perform interaction and escape without leaving a trace in standard
runtime process events if they use API-based methods.
stage: pod-exec-interaction
- id: ephemeral-pod-visibility
question: Did a pod exist only for a few seconds during the escape attempt?
remediation: Move from periodic inventory snapshots to an event-driven pod creation
stream.
requires: Continuous pod inventory (live stream)
risk: Snapshot-based inventory like kubernetes_pod may miss short-lived attack pods
used for one-off commands.
stage: privileged-pod-deployment
coverage:
- stage: workload-service-account-discovery
status: covered
steps:
- discovery-tool-prevalence
- stage: token-credential-harvesting
status: covered
steps:
- token-harvesting-activity
- stage: privileged-pod-deployment
status: covered
steps:
- privileged-pod-deployment
- blind_spot: missing-k8s-audit-logs
reason: Requires Kubernetes audit logs to see decoded requestURIs for exec commands;
not present in available hb_ surfaces.
stage: pod-exec-interaction
status: not_visible
- reason: Escape wrappers nsenter/chroot in URI format require API audit logs; hb_process_activity
may see them if run as host processes, but the API interaction is invisible.
stage: container-escape-attempt
status: not_visible
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: promote-to-detection
justification: Kubernetes container escape allows an attacker to pivot from a single
compromised container to the entire physical host, bypassing isolation boundaries.
methodology: model-assisted
trigger: intel-report
hypothesis: An intruder has harvested a service account token from a compromised pod
and is using it to deploy a privileged pod for container escape, bypassing standard
runtime process detection.
labels:
- hunt
- attack.t1613
- attack.t1552.006
- attack.t1610
- attack.t1609
- attack.t1611
name: Kubernetes Service Account Abuse and Escape
parameters:
discovery_commands:
default:
- kubectl
- kube-hunter
- kube-bench
- microk8s
- k3s
description: Common Kubernetes discovery tools and command fragments.
type: list[string]
k8s_secret_paths:
default:
- /var/run/secrets/kubernetes.io/serviceaccount/token
- /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
description: Paths to Kubernetes service account secrets.
from:
kind: article
observed: '2026-09-03'
ref: https://www.elastic.co/security-labs/threat-command/kubernetes-audit-logs-container-escape
type: list[path]
lookback_days:
default: '14'
description: Days of history to examine.
type: number
scope_hosts:
default: []
description: Specific hosts to narrow the hunt; leave empty for fleet-wide.
type: list[host]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.elastic.co/security-labs/threat-command/kubernetes-audit-logs-container-escape
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: Focus on namespaces hosting public-facing services. Use the software inventory
step to identify worker nodes running K8s system packages (deb/rpm only).
references:
- name: "Elastic Security Labs \u2014 How to correlate Kubernetes audit logs with\
\ container runtime data"
url: https://www.elastic.co/security-labs/threat-command/kubernetes-audit-logs-container-escape
related:
- hunt: cloud-control-plane-credential-abuse
reason: This hunt focuses on the Kubernetes plane; abuse of cloud provider identity
(IMDS) is a separate scenario.
relation: out-of-scope-alternative
scenario:
stages:
- name: Service account discovery
observables:
- kubectl get pods
- system:serviceaccount identifier
- API discovery verbs
slug: workload-service-account-discovery
tactic: discovery
techniques:
- T1613
- name: Service account token harvesting
observables:
- /var/run/secrets/kubernetes.io/serviceaccount/token
- /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
slug: token-credential-harvesting
tactic: credential-access
techniques:
- T1552.006
- name: Privileged pod deployment
observables:
- 'objectRef.resource: pods'
- 'kubernetes.audit.verb: create'
- 'privileged: true security context'
slug: privileged-pod-deployment
tactic: execution
techniques:
- T1610
- name: Interactive pod shell
observables:
- 'kubernetes.audit.objectRef.subresource: exec'
- requestURI containing command list
slug: pod-exec-interaction
tactic: execution
techniques:
- T1609
- name: Container escape via host utilities
observables:
- nsenter
- chroot
- requestURI containing escape wrappers
slug: container-escape-attempt
tactic: privilege-escalation
techniques:
- T1611
summary: An attacker leverages a compromised service account to perform discovery
and harvest authentication tokens from the container filesystem. Using these credentials,
they deploy a privileged pod and attempt a container escape using utilities like
nsenter and chroot, a multi-plane attack requiring correlation between runtime
telemetry and Kubernetes audit logs.
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
kubernetes:
category: siem
huntbase:
product: kubernetes
name: kubernetes
tlp: clear
type: investigation
---
# Kubernetes Service Account Abuse and Escape
This hunt correlates early-stage discovery and credential harvesting inside containers with follow-on control-plane abuse. It moves from process-level discovery and file-level token access to identifying privileged pod configurations that facilitate escape. By joining endpoint telemetry with Kubernetes pod manifests, we identify the complete progression from a beachhead to a potential host-level compromise.
## k8s-infrastructure-scope
<!-- Identify Kubernetes infrastructure -->
Locate hosts running Kubernetes or container runtimes to focus the hunt, filtering out unrelated system packages.
```sqlite target=endpoint role=scoping params=(scope_hosts=scope_hosts)
~~~yaml
expected: A list of hostnames hosting Kubernetes components. Silence means no K8s-related
system packages are installed.
reads:
- device_hostname
- package_name
- package_type
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT DISTINCT device_hostname FROM hb_software_inventory WHERE (LOWER(package_name) LIKE '%kube%' OR LOWER(package_name) LIKE '%docker%' OR LOWER(package_name) LIKE '%containerd%') AND (package_type = 'deb' OR package_type = 'rpm') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)
```
## gather-early-evidence
<!-- Gather discovery and harvesting evidence -->
parallel:
- → discovery-tool-prevalence
- → token-harvesting-activity
join: → early-stage-triage
## discovery-tool-prevalence
<!-- Discovery tool prevalence -->
Find evidence of cluster enumeration using common tools, checking for rare executions that indicate manual intervention.
```sqlite target=endpoint role=baseline params=(discovery_commands=discovery_commands, lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: A rare process execution indicating manual discovery. Silence suggests no
common tools were run via monitored process events.
prevalence:
by: device_hostname
key:
- process_name
- process_cmd_line
rare_below: 5
reads:
- process_name
- process_cmd_line
- device_hostname
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT process_name, process_cmd_line, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_process_activity WHERE (LOWER(process_name) LIKE '%/kubectl' OR LOWER(process_name) LIKE '%/kube-hunter' OR LOWER(process_name) LIKE '%/kube-bench' OR instr(',' || '{{discovery_commands}}' || ',', ',' || LOWER(process_name) || ',') > 0 OR LOWER(process_cmd_line) LIKE '%get pods%' OR LOWER(process_cmd_line) LIKE '%get secrets%') AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) GROUP BY process_name, process_cmd_line HAVING hosts <= 5 ORDER BY hosts
```
## token-harvesting-activity
<!-- Service account token harvesting -->
Detect access to Kubernetes service account tokens, which are the target for attackers seeking to abuse the API.
```sqlite target=endpoint role=detection-candidate params=(k8s_secret_paths=k8s_secret_paths, lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Processes reading tokens from /var/run/secrets. Access from non-system processes
is a high-fidelity indicator.
reads:
- device_hostname
- process_name
- process_cmd_line
- file_path
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_name, process_cmd_line, file_path, time FROM hb_file_activity WHERE instr(',' || '{{k8s_secret_paths}}' || ',', ',' || LOWER(file_path) || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)
```
## early-stage-triage
<!-- Triage early-stage activity -->
```agent target=hunter
cite: required
context:
- discovery-tool-prevalence
- token-harvesting-activity
max_iterations: 3
objective: Determine if the same container or host that ran Kubernetes discovery tools
also accessed sensitive service account tokens.
success_criteria: Identification of a compromised pod identity and host context.
tools:
- endpoint
- kubernetes
```
## privileged-pod-deployment
<!-- Identify privileged pod deployments -->
Find pods with high-risk configurations that facilitate container escape by sharing host namespaces.
```sqlite target=kubernetes role=triage params=(lookback_days=lookback_days)
~~~yaml
expected: Pods created with host namespace permissions. Silence means no pods with
these dangerous settings were detected in the current inventory.
reads:
- hostname
- host_pid
- host_network
- host_ipc
- creation_timestamp
- context_name
silence: evidence_of_absence
source: kubernetes_pod
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT hostname, host_pid, host_network, host_ipc, creation_timestamp, context_name FROM kubernetes_pod WHERE (host_pid = 1 OR host_network = 1 OR host_ipc = 1) AND creation_timestamp >= datetime('now', '-{{lookback_days}} days')
```
## final-intrusion-assessment
<!-- Final intrusion assessment -->
```agent target=hunter
cite: required
context:
- early-stage-triage
- privileged-pod-deployment
max_iterations: 4
objective: Weigh the early-stage discovery findings against the appearance of privileged
pods. Does the timing and identity suggest the discovery led to this deployment?
success_criteria: A final verdict of malicious | suspicious for the investigated hosts.
tools:
- endpoint
- kubernetes
```
## route-on-verdict
<!-- Route on verdict -->
if~: "the final-intrusion-assessment verdict is malicious for at least one host following token harvesting" (confidence: high, judge=hunter)
then: → isolate-host
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: missing-k8s-audit-logs)
else: → close-out
## isolate-host
<!-- Isolate host and revoke SA -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the endpoint running the Kubelet. Coordinate with DevOps to delete the privileged pod and revoke the compromised service account token immediately.
```
→ analyst-review
## analyst-review
<!-- Analyst review -->
```manual target=analyst
Review the process timeline leading to token harvesting. Check for other pods created by the same service account. Verify if escape wrappers like nsenter were executed by checking direct host process activity.
```
→ close-out
## close-out
<!-- Close out -->
```manual target=analyst
Record the findings. If token harvesting was confirmed, promote the file-activity query to a permanent detection rule.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.