Kubetier
← Permission Reference
T0

get nodes/proxy

Lets an attacker execute commands in any pod on the node.

WebSocket exec handshakes over HTTP GET map to the get verb, so get alone on nodes/proxy bypasses pods/exec and the audit log.

Proxies to the kubelet API on port 10250.

Combined with create on nodes, it also drives fake-Node SSRF, making the API server proxy back to itself.

What that returns depends on how the cluster binds the apiserver-kubelet-client certificate.

API Group
(core)
Scope
cluster
Audit Level
RequestResponse

Escalation Paths

  1. Connect to the exec endpoint via WebSocket.

    The HTTP GET upgrade handshake maps to the 'get' verb at the kubelet, so get nodes/proxy alone is sufficient

  2. The kubelet never performs a secondary 'create' check for the exec operation that follows the handshake

  3. Path A, via API server proxy, uses curl with Upgrade: websocket headers.

    Auth is logged as a nodes/proxy check only, the actual exec is invisible to audit

  4. Path B is direct to the kubelet on port 10250, using websocat wss://$NODE_IP:10250/exec/....

    Zero API server audit events are generated

  5. Execute arbitrary commands in any pod on the node, and steal service account tokens to pivot

Additional rights needed:

none

Note:

There is no CVE.

The security team calls this working as intended (Jan 2026).

CVE-2020-8559 and CVE-2022-3294 are separate patched issues.

WebSocket exec bypass has no fix.

KEP-2862 adds narrower subresources but does not cover exec/attach/portforward.

K8s docs ↗
  1. Register a fake Node object with status.addresses[InternalIP] = API server internal IP and daemonEndpoints.kubeletEndpoint.Port = 6443

  2. Proxy a request through the fake node by running kubectl get --raw /api/v1/nodes/evil-node/proxy/api/v1/secrets

  3. The API server proxies outbound to that address using its apiserver-kubelet-client certificate, so it authenticates to itself

  4. Before kubeadm 1.29 the certificate carried O=system:masters.

    From 1.29 it carried O=kubeadm:cluster-admins, a group bound to cluster-admin, so the inner request still returned full cluster access

  5. Current kubeadm binds the certificate to system:kubelet-api-admin instead, narrowing the inner request to the node API (nodes/proxy, metrics, stats, log) rather than cluster-admin

  6. The redirection itself is unchanged by that fix, so the API server can still be pointed at an attacker-chosen address

  7. Post-fix, the gain is network position rather than RBAC, since triggering it already requires get on nodes/proxy.

    The API server reaches addresses the caller cannot, under its own identity

Additional rights needed:

Note:

Those releases narrow the certificate to system:kubelet-api-admin, which removes the cluster-admin outcome but not the redirection.

kubernetes#119270 is still open, reopened for audit re-evaluation.

1.32 reached end of life first, so it stays vulnerable.

No CVE covers this path.

K8s docs ↗
  1. Register a fake Node with a DNS rebinding hostname that initially resolves to a valid public IP

  2. API server checks isProxyableHost before connecting.

    At check time, DNS resolves to the allowed IP

  3. By connect time, DNS flips to 127.0.0.1 or 169.254.x.x.

    The blocklist check is not re-run (TOCTOU)

  4. SSRF reaches loopback services such as kube-proxy :10249 configz, etcd :2379, and control-plane localhost ports

Additional rights needed:

Note:

This is a wontFix.

Fixing requires IP pinning between check and connect, which breaks split-horizon DNS.

Mitigation is dnsmasq min-cache-ttl.

K8s docs ↗
K8s docs ↗