Skip to content
Sedat Özdemir
Writing

devsecops

A Fox in the K8s Henhouse: RBAC Vulnerabilities and the Art of Container Escape

It's 4:12 AM, and a simple customer-feedback pod is suddenly trying to list all secrets in the production cluster. Let's dive into how over-privileged service accounts and lazy RBAC configurations turn a minor entry point into a full-blown cluster takeover.

Sedat Özdemir
· 4 dk read

It was exactly 04:12 AM. I woke up to my phone vibrating with a 'Critical' alert. In one of our testCompany production clusters, I saw a simple, internet-facing 'customer-feedback' pod doing something it should never do: it was trying to query the entire list of 'secrets' via the kube-apiserver. While rubbing my eyes, my first thought was clear: someone is inside, and they are currently trying to move laterally through the cluster.

Even though I look at things from a Red Teamer's perspective, figuring out what was happening inside that pod that night turned into a total detective story. Most folks assume Kubernetes (K8s) is secure right out of the box, but in reality, we have a massive orchestra here—and if the conductor is drunk, the concert becomes a disaster.

The First Hole: Default ServiceAccounts and 'Over-Generous' Roles

The attacker's first stop that night was the ServiceAccount token embedded within the pod. In Kubernetes, every pod is born with a service account unless specified otherwise. If you don't properly restrict the permissions of this pod, the moment an attacker breaches it, they grab that token and start asking the cluster, 'Who am I and what can I do?'

The first command the attacker used probably looked something like this:

# Checking permissions with the token inside the pod
KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $KUBE_TOKEN" https://127.0.0.1:6443/api/v1/namespaces/default/secrets

Under normal conditions, this request should return a '403 Forbidden'. But in our scenario, the dev team—probably to 'keep things moving fast'—had defined a role similar to cluster-admin for this service account. And that's where the nightmare begins.

RBAC: Don't Be Afraid to Be Stingy with Permissions

Role-Based Access Control (RBAC) is where the most mistakes happen in Kubernetes. Usually, when creating a ClusterRoleBinding, the logic follows 'Let’s give it all permissions for now, we'll fix it once it works' instead of 'What specific permissions does it actually need?'.

Here is an example of a malicious ClusterRoleBinding (Defanged):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: feedback-pod-admin-binding
subjects:
- kind: ServiceAccount
  name: default
  namespace: production
roleRef:
  kind: ClusterRole
  name: cluster-admin # THIS IS WHERE WE HIT THE WALL
  apiGroup: rbac.authorization.k8s.io

The cluster-admin role gives the attacker 'God Mode' over the cluster. If a pod only needs to read configmaps in its own namespace, but you give it permission to see secrets across the entire cluster, you're essentially leaving the keys in the door.

The Defense Recipe:

  • Always use the automountServiceAccountToken: false setting. If your pod doesn't need to talk to the API, there is no reason to mount that token inside.
  • Apply the 'Least Privilege' principle. Define only the necessary verbs (get, list) and the necessary resources (pods, deployments).

Escaping the Container: hostPath and Privileged Pods

In that night's crisis, the attacker didn't stop at the API. They took it a step further to try and escape to the node itself...

Related posts