A developer pushes a misconfigured Helm chart. Forty minutes later, an attacker has root on the underlying node — and from there, full access to the cloud account hosting the entire production cluster.

This is not a theoretical scenario. Kubernetes-related attacks increased 282% in the past year, with the IT sector accounting for over 78% of observed activity. Containers introduced speed and scale — they also introduced a new attack surface that most teams don’t fully understand.

TL;DR

  • RBAC misconfigurations account for 35%+ of Kubernetes breaches
  • Privileged containers and hostPath mounts are trivial container escapes
  • Service account tokens are often over-permissioned and can pivot to cloud accounts
  • Recent runC CVEs (2025) allow container escape via symlink manipulation
  • Runtime security (Falco), strict Pod Security Admission, and network policies are the core defensive layer

Why Kubernetes Is a High-Value Target

Kubernetes (K8s) is the de facto standard for running containerized workloads. It orchestrates thousands of containers, manages secrets, handles network routing, and controls access to cloud resources.

From an attacker’s perspective, a single foothold in a Kubernetes cluster can mean:

  • Code execution across dozens of workloads (lateral movement between pods)
  • Access to secrets and environment variables (credentials, API keys, database passwords)
  • Escape to the host node (bypassing container isolation)
  • Cloud account takeover (via over-permissioned service accounts bound to cloud IAM)

The complexity of K8s is its biggest security weakness. Most teams configure it for functionality, not security.


The Attack Surface: A Map

Before diving into specific techniques, it helps to understand where the attack surface lies:

LayerAttack surface
Container imageMalicious/backdoored images, outdated base images, embedded secrets
Container runtimePrivileged containers, dangerous mounts, runC vulnerabilities
Kubernetes API serverExposed externally, weak authentication, overpermissioned service accounts
RBACExcessive permissions, wildcard roles, cluster-admin on service accounts
SecretsBase64-encoded in etcd, injected as env vars, accessible to any pod in namespace
NetworkNo network policies = east-west traffic unrestricted between all pods
Cloud IAMService account → workload identity → cloud IAM role → cloud account access

Attack Techniques

1. Container Escape via Privileged Mode

A privileged container runs with --privileged flag or securityContext.privileged: true. This disables almost all Linux security isolation — the container shares the host’s namespaces and has near-root access to the host.

Escape is trivial:

Terminal window
# Inside privileged container
# Mount the host filesystem
mkdir /host
mount /dev/sda1 /host
# Chroot into host
chroot /host bash
# You now have root on the node

This attack works because privileged mode grants access to host devices directly. Any workload that legitimately needs --privileged (e.g., some monitoring agents) should be treated as a high-risk component.


2. Container Escape via hostPath Mounts

Even without full privileged mode, mounting the host filesystem via hostPath can lead to escape:

# Dangerous pod spec
volumes:
- name: host-root
hostPath:
path: /
type: Directory
volumeMounts:
- mountPath: /host
name: host-root

With access to the host root at /host, an attacker can:

  • Read /host/etc/shadow — password hashes
  • Write cron jobs to /host/etc/cron.d/ — persistence
  • Read kubelet credentials from /host/var/lib/kubelet/

3. runC Vulnerabilities (CVE-2025-31133 and others)

runC is the low-level container runtime that Docker and Kubernetes call to actually create containers. It sets up Linux namespaces, cgroups, and mounts.

In 2025, three critical runC vulnerabilities were disclosed:

  • CVE-2025-31133 — runC uses /dev/null bind-mounts to mask sensitive host paths. If an attacker replaces /dev/null with a symlink during container initialization, runC can bind-mount an attacker-controlled path read-write into the container — enabling writes to /proc and full container escape.
  • CVE-2025-52565 — affects runC 1.0.0-rc3 and later
  • CVE-2025-52881 — affects all runC versions

Exploitation requires the ability to start containers with custom mount configurations — achievable via malicious container images or Dockerfiles. Fixes are available in runC versions 1.2.8, 1.3.3, and 1.4.0-rc.3.

Detection: monitor for suspicious symlink creation and unusual bind-mount patterns in container initialization.


4. Service Account Token Abuse

Every Kubernetes pod automatically gets a service account token mounted at:

/var/run/secrets/kubernetes.io/serviceaccount/token

This token authenticates to the Kubernetes API server. If the service account has excessive permissions (common), an attacker with pod code execution can pivot to cluster-level operations:

Terminal window
# Read the token from inside a compromised pod
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER=https://kubernetes.default.svc
# Query the API — what can this service account do?
curl -s $APISERVER/api/v1/namespaces/default/secrets \
--header "Authorization: Bearer $TOKEN" \
--insecure

The most dangerous pattern: default service accounts with cluster-admin binding. This single misconfiguration turns any container compromise into full cluster takeover.

In cloud environments (EKS, GKE, AKS), this gets worse — Kubernetes service accounts can be mapped to cloud IAM roles via Workload Identity. A compromised pod can then call AWS/GCP/Azure APIs with the permissions of that cloud role.


5. RBAC Misconfigurations

RBAC (Role-Based Access Control) is how Kubernetes controls who can do what. Misconfigurations account for 35%+ of Kubernetes breaches.

The most dangerous patterns:

# DANGEROUS: wildcard permissions
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
# DANGEROUS: binding cluster-admin to a service account
subjects:
- kind: ServiceAccount
name: my-app
namespace: default
roleRef:
kind: ClusterRole
name: cluster-admin

Less obvious but equally dangerous: create permission on pods/exec lets an attacker exec into any pod in the namespace. get on secrets reads all secrets. create on pods allows launching a privileged pod.


6. Kubernetes API Server Exposure

The API server should never be directly exposed to the internet. Yet tools like Shodan regularly find thousands of exposed K8s API servers. If anonymous authentication is enabled (it shouldn’t be, but sometimes is), attackers can query the cluster with no credentials.

Even without anonymous access, a valid kubeconfig file or service account token is enough. These often leak via:

  • Git repositories (committed ~/.kube/config or CI/CD secrets)
  • Container images with embedded credentials
  • Misconfigured RBAC allowing secret reads

Blue Team: Detection

Runtime Security with Falco

Falco is the industry-standard runtime security tool for Kubernetes. It monitors system calls from containers and alerts on suspicious behavior:

# Falco rule: detect shell spawned inside container
- rule: Shell Spawned in Container
desc: A shell was spawned inside a container
condition: >
spawned_process and container and
shell_procs and not shell_in_allowlist
output: >
Shell spawned in a container
(user=%user.name container=%container.name
image=%container.image.repository cmd=%proc.cmdline)
priority: WARNING

Key behaviors to detect with Falco:

  • Shell execution in production containers
  • Reads of sensitive files (/etc/shadow, /proc/1/environ)
  • Unexpected outbound network connections
  • kubectl exec sessions
  • Privilege escalation attempts (setuid, setgid)

Audit Logging

Enable Kubernetes audit logs and ship them to your SIEM. Critical events to alert on:

EventRisk
verb=create, resource=pods, spec.privileged=truePrivileged pod launch
verb=exec, resource=podsInteractive shell in pod
verb=get, resource=secretsSecret reads
verb=create, resource=clusterrolebindingsNew cluster-admin binding
verb=list, resource=*Reconnaissance by service account
Anonymous requests to API serverUnauthenticated access attempt

Network Monitoring

Without network policies, all pods can communicate freely — making lateral movement trivial. Monitoring east-west traffic with tools like Cilium Hubble or Calico flow logs gives visibility into pod-to-pod communication patterns and can reveal unexpected connections.


Hardening: What to Lock Down

Pod Security Admission

Kubernetes replaced the deprecated PodSecurityPolicy with Pod Security Admission (PSA) in v1.25+. Apply baseline or restricted profiles at the namespace level:

apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest

The restricted profile blocks: privileged containers, host namespaces, hostPath mounts, running as root, and privilege escalation.


RBAC Hardening

The principle of least privilege, applied to Kubernetes:

# Good: narrow, specific permissions
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list"]
resourceNames: ["my-specific-deployment"] # limit to one resource

Audit existing RBAC with:

Terminal window
# List all ClusterRoleBindings and their subjects
kubectl get clusterrolebindings -o json | \
jq '.items[] | {name: .metadata.name, subjects: .subjects, role: .roleRef.name}'
# Find service accounts with cluster-admin
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'

Secrets Management

Do not store secrets as Kubernetes Secrets if you can avoid it — they are just base64-encoded in etcd, and any pod with get secrets permission can read them. Better options:

  • External Secrets Operator + HashiCorp Vault / AWS Secrets Manager / Azure Key Vault
  • Sealed Secrets for GitOps workflows (asymmetrically encrypted)
  • CSI Secret Store Driver — mount secrets from external vaults directly as files

Also: disable auto-mounting of service account tokens for pods that don’t need API access:

spec:
automountServiceAccountToken: false

Network Policies

By default, all pods can reach all other pods. Network policies define explicit allow rules:

# Deny all ingress/egress by default in a namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

Then add explicit allows only for needed traffic. This limits blast radius if a pod is compromised — the attacker can’t freely connect to other services.


Image Security

Container images are often the initial foothold:

  • Never use latest tag — pin to a specific digest: image: nginx@sha256:abc123...
  • Scan images for vulnerabilities with Trivy, Grype, or Snyk before deploying
  • Admit only trusted registries — use OPA/Gatekeeper or Kyverno policies to block images from unknown registries
  • Use distroless or minimal base images — less tooling inside the container = harder for attackers to operate

What You Can Do Today

If you manage a Kubernetes cluster, these five actions have the highest impact:

  1. Run kubectl get clusterrolebindings — find anything bound to cluster-admin that shouldn’t be. Fix it.
  2. Check for privileged pods: kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged==true) | .metadata.name'
  3. Enable Pod Security Admission on all namespaces with at least the baseline profile.
  4. Add automountServiceAccountToken: false to pods that don’t need API access.
  5. Update runC to version 1.2.8+ to patch CVE-2025-31133 and related container escape vulnerabilities.
  6. Deploy Falco for runtime threat detection — it takes under an hour to install via Helm and gives immediate visibility.


Sources