Kubernetes¶
Enumerate and exploit Kubernetes API servers hosting ML/AI workloads.
Overview¶
The k8s module probes a Kubernetes API server directly (no kubectl/client-go dependency). It assesses anonymous / unauthenticated RBAC posture, maps the current identity's authorization, enumerates workloads and custom resources, exfiltrates Secrets, harvests model-artifact locations, executes commands inside pods, and performs in-cluster lateral movement by stealing a pod's service-account token and re-authenticating as it.
The headline weakness it finds is the classic catastrophic misconfiguration: anonymous authentication enabled (--anonymous-auth=true) plus a read (or broader) ClusterRole bound to system:anonymous. On ML clusters that turns an unauthenticated request into model-registry token theft and, when the binding includes pods/exec, in-pod remote code execution — and from there, theft of a pod's service-account token to escalate to a more-privileged identity.
Scope (single API server; in-cluster traversal). Every verb operates against exactly one --target API server, but it traverses identities and namespaces within that cluster: enum/secret-read take --all-namespaces, access-review maps any identity's permissions, and sa-loot steals a pod's SA token and re-authenticates as it. It stops at the cluster boundary — no cross-cluster / cluster-mesh movement. (Kubelet :10250 pivots, cloud-metadata SSRF, and host escape are out of scope here — they cannot be honestly proven on a single-node target.)
TLS. k3s and most clusters serve a self-signed API server certificate on :6443; pass --insecure to skip verification.
Subcommands¶
Read-only (no --force-exploit required)¶
| Subcommand | Description |
|---|---|
rbac-probe |
Assess anonymous / unauthenticated API access and classify the posture |
access-review |
Map what the current identity is authorized to do (SelfSubjectRulesReview) |
enum |
Enumerate workloads (pods, deployments, services, configmaps) and custom resources (--all-namespaces for cluster-wide) |
Gated (requires --force-exploit)¶
| Subcommand | Description |
|---|---|
secret-read |
List and base64-decode every Secret in a namespace (--all-namespaces for cluster-wide) to plaintext |
artifact-read |
Harvest model-artifact locations from ConfigMaps and InferenceService specs |
pod-exec |
Execute a command inside a pod over the API server exec stream (in-pod RCE) |
sa-loot |
Steal a pod's service-account token, re-authenticate as it, and detect privilege escalation |
Flags¶
Common (persistent)¶
| Flag | Required | Description |
|---|---|---|
--target, -t |
Yes | Kubernetes API server URL (e.g. https://127.0.0.1:6443) |
--namespace |
No | Namespace to target (default default) |
--header |
No | Additional HTTP header(s) in Key: Value format — e.g. a bearer token. Repeatable. |
--insecure |
No | Skip TLS verification (needed for self-signed API server certs) |
Per-subcommand¶
| Subcommand | Flags |
|---|---|
enum, secret-read |
--all-namespaces, -A (operate over every readable namespace instead of just --namespace) |
pod-exec |
--pod (default: first pod in the namespace), --container (default: the pod's first container), --command (comma-separated argv, default id) |
sa-loot |
--pod (default: first pod in the namespace), --container (default: the pod's first container) |
Proof model¶
Findings carry proof metadata (proof_stage / proof_strength) and are honest in both directions. rbac-probe classifies the response, never assuming a weakness:
| Posture | Meaning | Severity |
|---|---|---|
anonymous-open |
A sensitive resource (namespaces/secrets/pods) is readable without credentials | High, influenced |
authenticated-restricted |
An anonymous identity exists but RBAC denies the resource (HTTP 403) | Info, reachable |
auth-enforced |
Anonymous reads are rejected with HTTP 401 — not weak | Info, reachable |
The gated verbs only claim success when it is real: secret-read reports read-confirmed only on a decoded Secret, and pod-exec reports exploited only when the exec stream returns command output — a 401/403 that prevents the exec stream is reported honestly as reachable (denied), not as RCE. sa-loot claims privilege escalation (execution-confirmed) only when the stolen identity can genuinely write (create/update/delete) where the anonymous foothold is read-only; a stolen token that is itself read-only is read-confirmed ("token stolen, no escalation"), and a token that can't be read is reachable. The escalation is confirmed via SelfSubjectRulesReview (the write capability is not exercised), and the captured token is redacted from output.
Verbs¶
rbac-probe (read-only)¶
Sends credential-free reads to sensitive endpoints (/api/v1/namespaces, /api/v1/secrets, /api/v1/pods, /apis/apps/v1/deployments) plus the discovery documents, and classifies the posture from the status codes (200 = anonymous access, 401 = enforced, 403 = anonymous identity but restricted).
access-review (read-only)¶
Asks the API server, as the current identity (anonymous, or a token via --header "Authorization: Bearer <jwt>"), exactly what it can do in --namespace via SelfSubjectRulesReview — a precise authorization map, not a probe-by-request heuristic. An unauthenticated caller that cannot self-review (401/403, e.g. system:anonymous) is reported honestly as such (Info, reachable); an identity that can write is High (incl. WRITE).
# anonymous (typically can't self-review)
aipostex k8s --target https://127.0.0.1:6443 --insecure access-review --namespace ml-prod
# as a captured/supplied identity
aipostex k8s --target https://127.0.0.1:6443 --insecure access-review --namespace ml-prod --header "Authorization: Bearer <jwt>"
enum (read-only)¶
Lists pods, deployments, services, and configmaps in --namespace and the cluster's CustomResourceDefinitions, highlighting ML-platform CRDs (KServe inferenceservices, Kubeflow, Seldon, Ray). Endpoints denied by RBAC are skipped, so a partial grant still yields what it exposes. Pass --all-namespaces/-A to enumerate every namespace the identity can read.
secret-read (gated)¶
Lists every Secret in --namespace and base64-decodes its data to plaintext — model-registry tokens, cloud object-store credentials, database passwords.
aipostex k8s --target https://127.0.0.1:6443 --insecure secret-read --namespace ml-prod --force-exploit
artifact-read (gated)¶
Reads ConfigMaps and InferenceService specs to recover model names, registry endpoints, and object-store storageUris — the coordinates an attacker needs to pull proprietary models.
aipostex k8s --target https://127.0.0.1:6443 --insecure artifact-read --namespace ml-prod --force-exploit
pod-exec (gated)¶
Opens an exec stream (WebSocket v5.channel.k8s.io) into a pod and runs --command. If --pod is omitted, the first pod in --namespace (preferring a component=model-server pod) is targeted. Arbitrary in-pod command execution is the takeover step.
aipostex k8s --target https://127.0.0.1:6443 --insecure pod-exec --namespace ml-prod --command id --force-exploit
sa-loot (gated)¶
In-cluster lateral movement. Execs into a pod (--pod, or the first pod in --namespace), reads its mounted service-account token (/var/run/secrets/kubernetes.io/serviceaccount/token), re-authenticates to the API server as that ServiceAccount, and maps what it can do via SelfSubjectRulesReview. If the stolen identity can write where the anonymous foothold is read-only, that is a privilege escalation (Critical, execution-confirmed). The token is redacted from output; the write capability is reported, not exercised.
Detection & templates¶
The discovery fingerprint identifies a Kubernetes API server on :6443 from /version (gitVersion, e.g. v1.31.5+k3s1) when anonymous reads are allowed, and from the 401 Unauthorized Status object when they are not. Three k8s-* vulncheck templates run in assess --mode full:
k8s-enum-001-anonymous-api— anonymous/version+/apidiscovery (detection).k8s-rbac-001-anonymous-namespace-list— anonymous namespace enumeration (detection).k8s-exploit-001-anonymous-secret-exfil— anonymous cluster-wide Secret data exfiltration (exploit).
Examples¶
# Posture first (read-only), then enumerate cluster-wide and map your own access
aipostex k8s --target https://127.0.0.1:6443 --insecure rbac-probe
aipostex k8s --target https://127.0.0.1:6443 --insecure enum --all-namespaces
aipostex k8s --target https://127.0.0.1:6443 --insecure access-review --namespace ml-prod
# Active exfiltration and execution (gated)
aipostex k8s --target https://127.0.0.1:6443 --insecure secret-read --all-namespaces --force-exploit
aipostex k8s --target https://127.0.0.1:6443 --insecure pod-exec --namespace ml-prod --command id --force-exploit
# In-cluster lateral movement: steal a pod's SA token and escalate
aipostex k8s --target https://127.0.0.1:6443 --insecure sa-loot --namespace ml-prod --force-exploit
# Network discovery + templated assessment of an API server
aipostex assess network --target 10.0.0.0/24 --ports 6443 --insecure --mode full