The breach isn’t the problem. The spread is. →Free Assessment

The Privilege Escalation Paths Nobody Audits

Most organizations secure their Kubernetes clusters by writing roles and bindings, running through a checklist, and moving on. That's the first mistake.

RBAC is not a configuration you complete: it's an attack surface you maintain. A single over-permissive binding, an auto-mounted ServiceAccount token, or a misconfigured IRSA trust policy can hand an attacker a path from low-privilege foothold to full cluster compromise. Once there, the blast radius is total: every workload, every secret, every connected cloud account.

What makes this hard isn't complexity: it's invisibility. The most dangerous escalation paths don't look dangerous in isolation. pods/exec seems like a reasonable developer permission. Granting secrets: get seems harmless. But combined, these become a step-by-step path to node takeover, and standard security tooling rarely flags them.

What this post covers:

  • The permission combinations that enable privilege escalation, and why they're so commonly granted

  • How ServiceAccount tokens and IRSA misconfigurations bridge Kubernetes and cloud IAM

  • Practical audit commands and detection rules you can run today

  • Remediation patterns that enforce least-privilege without breaking developer workflows

The RBAC Mental Model: And Where It Breaks Down

Kubernetes RBAC operates on a simple allow-only model: subjects (users, groups, ServiceAccounts) are granted permissions via RoleBindings or ClusterRoleBindings. There are no explicit deny rules. Any permission granted, even unintentionally, is permanently open unless actively revoked.

The problem is not that RBAC is broken. It is that:

  • Defaults are permissive.

    The

    system:authenticated

    group gets read access to many non-sensitive-looking APIs that can still be abused.

  • Operators grant broad permissions for convenience.

    cluster-admin

    bindings are routinely created during debugging sessions and never removed.

  • Indirect escalation paths exist.

    Some permission combinations that look innocuous individually combine to form full privilege escalation chains.

The cluster-admin ClusterRoleBinding Problem

The most common misconfiguration in production clusters is the over-assignment of cluster-admin. This happens through:

# Classic 'just get it working' binding — seen constantly in the wild kubectl create clusterrolebinding developer-admin \ --clusterrole=cluster-admin \ --user=developer@company.com # Or worse — binding to a service account used by a CI deployment kubectl create clusterrolebinding ci-admin \ --clusterrole=cluster-admin \ --serviceaccount=ci:github-runner

⚠️ High Risk: The CI runner binding is particularly dangerous: if your runner pod is compromised via a malicious GitHub Action or supply chain attack, the attacker immediately inherits cluster-admin privileges.

Detection: Audit existing ClusterRoleBindings

kubectl get clusterrolebindings -o json | jq ' .items[] | select(.roleRef.name == "cluster-admin") | { name: .metadata.name, subjects: .subjects } '

Detection with Falco

- rule: Cluster Admin Binding Created desc: Detect creation of ClusterRoleBinding to cluster-admin condition: > kube_audit and ka.verb in (create, update) and ka.target.resource = clusterrolebindings and ka.req.binding.role = cluster-admin output: > Cluster admin binding created (user=%ka.user.name binding=%ka.target.name) priority: CRITICAL

Dangerous Permission Combinations

Individual permissions often seem harmless. The risk emerges when combined. Here are the most commonly overlooked combinations.

pods/exec: The Interactive Shell Gift

The pods/exec permission allows a subject to exec into any running pod in the permitted namespace. This is routinely granted to developers for debugging.

# This looks reasonable... rules: - apiGroups: [""] resources: ["pods/exec"] verbs: ["create"]

The escalation path:

  1. Attacker gains access to any principal with

    pods/exec

  2. Lists pods to find one running as root or with a privileged security context

  3. Execs into the pod

  4. Escapes to the node via volume mounts,

    /proc/1/root

    , or privileged capabilities

  5. Gains node-level access and can reach the kubelet API

Find privileged pods:

kubectl get pods -A -o json | jq ' .items[] | select( .spec.containers[].securityContext.runAsUser == 0 or .spec.containers[].securityContext.privileged == true ) | {namespace: .metadata.namespace, name: .metadata.name} '

Once inside a privileged pod:

# Mount the host filesystem and escape to node nsenter --target 1 --mount --uts --ipc --net --pid -- bash # Result: root shell on the underlying node

secrets GET: Credential Harvesting at Scale

The ability to get or list secrets in a namespace, or cluster-wide, is a high-value permission frequently granted without full awareness of its impact. In a typical production cluster, secrets routinely contain database credentials, API keys, cloud provider credentials, TLS private keys, and service account tokens.

# Dump all secrets in a namespace kubectl get secrets -n production -o json | jq ' .items[] | { name: .metadata.name, type: .type, keys: (.data | keys) }' # Decode a specific secret value kubectl get secret db-credentials \ -o jsonpath='{.data.password}' | base64 -d

Audit who has secrets access with kubectl-who-can (krew plugin):

kubectl who-can get secrets -n production

create pods + serviceAccountName: Token Hijacking

If a subject can create pods and the namespace contains ServiceAccounts with elevated permissions, they can create a pod that mounts a privileged ServiceAccount token. This is one of the most overlooked privilege escalation vectors.

# Attacker creates a pod targeting a high-privilege ServiceAccount apiVersion: v1 kind: Pod metadata: name: escalation-pod spec: serviceAccountName: high-privilege-service-account containers: - name: attacker image: curlimages/curl command: ["sleep", "infinity"]

Inside the pod, the mounted SA token can call the Kubernetes API with elevated rights:

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) curl -H "Authorization: Bearer $TOKEN" \ https://kubernetes.default.svc/api/v1/namespaces/kube-system/secrets

Required permissions for this attack: pods: create in target namespace + knowledge of a high-privilege ServiceAccount name (easily listed with serviceaccounts: list).

ServiceAccount Token Abuse

Auto-mounted Tokens

By default, Kubernetes automatically mounts a ServiceAccount token into every pod. This remains a misconfiguration risk when automountServiceAccountToken is not explicitly set to false.

# Check which pods have tokens auto-mounted kubectl get pods -A -o json | jq ' .items[] | select(.spec.automountServiceAccountToken != false) | { namespace: .metadata.namespace, pod: .metadata.name, serviceAccount: .spec.serviceAccountName } '

From inside a compromised pod, the token is immediately usable:

APISERVER=https://kubernetes.default.svc TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt # Enumerate what this SA can access curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" \ $APISERVER/api/v1/namespaces/default/pods

OIDC / IRSA Abuse in Cloud Environments

Modern cloud Kubernetes platforms (EKS, GKE, AKS) support Workload Identity / IRSA, mapping ServiceAccounts to cloud IAM roles. Misconfigured trust policies create a bridge between Kubernetes and cloud infrastructure that attackers can cross from any pod.

Overly permissive EKS IRSA trust policy (allows any pod in any namespace):

{ "Condition": { "StringLike": { "oidc.eks.us-east-1.amazonaws.com/.../id/EXAMPLE:sub": "system:serviceaccount:*:*" // Wildcard — never use this } } }

Correct trust policy, scoped to a specific namespace and ServiceAccount:

"StringEquals": { "oidc.eks.us-east-1.amazonaws.com/.../id/EXAMPLE:sub": "system:serviceaccount:production:my-specific-app" }

The kubectl exec Pivot Chain

Beyond permission abuse, kubectl exec is a powerful lateral movement tool for an attacker who has compromised developer credentials or a CI/CD kubeconfig.

Compromised developer credentials (kubeconfig) → kubectl exec into running application pod → Read mounted secrets / environment variables → Use credentials to access databases, S3, external APIs → Or: escape to node if pod is privileged → Access kubelet API on the node → List and exec into ALL pods on that node

Detecting exec with Falco

- rule: Terminal Shell in Container desc: A shell was spawned via exec in a container condition: > spawned_process and container and shell_procs and proc.pname in (docker, runc, containerd-shim) output: > Shell spawned in container (user=%user.name container=%container.name image=%container.image.repository pod=%k8s.pod.name ns=%k8s.ns.name cmdline=%proc.cmdline) priority: WARNING

Auditing Your Cluster: A Practical Checklist

RBAC Audit with rakkess and kubectl-who-can

# Install via krew kubectl krew install access-matrix kubectl krew install who-can # Full access matrix for your current identity kubectl access-matrix # Who can delete pods in kube-system? kubectl who-can delete pods -n kube-system # Who can create ClusterRoleBindings? kubectl who-can create clusterrolebindings

Find Escalation Paths with rbac-police

git clone https://github.com/PaloAltoNetworks/rbac-police cd rbac-police && go build -o rbac-police . # Evaluate against live cluster ./rbac-police eval lib/police.rego # Example output: # CRITICAL: ServiceAccount 'github-runner' in 'ci' can escalate to cluster-admin # via: create pods + serviceAccountName targeting 'admin-sa' in 'kube-system'

CIS Benchmark with kube-bench

docker run --pid=host --userns=host --rm \ -v /etc:/etc:ro -v /var:/var:ro \ aquasec/kube-bench:latest \ --benchmark cis-1.8 2>/dev/null # Focus on CIS controls 5.1.1 through 5.1.6 for RBAC

Remediation: Least-Privilege RBAC Patterns

Disable Auto-mounting at the ServiceAccount Level

apiVersion: v1 kind: ServiceAccount metadata: name: my-app namespace: production automountServiceAccountToken: false # Opt out by default

Use Short-lived Projected Service Account Tokens

volumes: - name: kube-api-token projected: sources: - serviceAccountToken: path: token expirationSeconds: 1800 # 30 minutes audience: kubernetes.default.svc

Enforce RBAC Policy with OPA/Gatekeeper

package kubernetes.admission deny[msg] { input.request.kind.kind == "ClusterRoleBinding" input.request.object.roleRef.name == "cluster-admin" not is_break_glass_subject(input.request.object.subjects) msg := "cluster-admin bindings are prohibited outside break-glass accounts" } is_break_glass_subject(subjects) { subject := subjects[_] subject.name == "break-glass-admin" }

Restrict pods/exec via Admission Webhook

apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: restrict-exec webhooks: - name: restrict-exec.security.internal rules: - apiGroups: [""] apiVersions: ["v1"] operations: ["CONNECT"] resources: ["pods/exec"] namespaceSelector: matchExpressions: - key: environment operator: In values: ["production"]

MITRE ATT&CK for Containers Mapping

Technique

ATT&CK ID

Description

Container Administration Command

T1609

kubectl exec for interactive access

Valid Accounts: Cloud Accounts

T1078.004

IRSA/Workload Identity abuse

Escape to Host

T1611

Privileged pod breakout

Unsecured Credentials: Container API

T1552.007

Kubernetes secret harvesting

Account Manipulation

T1098

Creating ClusterRoleBindings

Summary: What Good Looks Like

A mature Kubernetes RBAC posture includes:

  • Regular automated audits of all ClusterRoleBindings and RoleBindings, flagging

    cluster-admin

    assignments for human review

  • No wildcard permissions (

    *

    ) in any role that is not a system role

  • ServiceAccount token auto-mounting disabled by default, enabled only where explicitly required

  • Runtime detection via Falco or similar, with alerting on exec, secret access, and new ClusterRoleBinding creation

  • IRSA / Workload Identity trust policies scoped to specific namespace + ServiceAccount pairs, never wildcarded

  • OPA/Gatekeeper policies enforcing RBAC hygiene at admission time, preventing dangerous configurations from reaching the cluster

Bottom Line: RBAC is a strong foundation, but it requires continuous auditing, not a one-time setup. The escalation paths described here are not theoretical; they are consistently found in production clusters and actively exploited in the wild.

Share This Article

Featured Categories

Secure The Connections Between Your Clouds and Cloud Workloads

Leverage a security fabric to meet compliance and reduce cost, risk, and complexity.

Cta pattren Image