# auditing-kubernetes-rbac-privilege-escalation

Finds over-permissive RBAC roles and service-account token abuse paths in a Kubernetes cluster using kubectl auth can-i, rbac-police, kubectl-who-can, and rakkess, tracing which subjects can escalate toward cluster-admin. Use when reviewing who can escalate privileges in a cluster, hunting exploitable RoleBindings during an authorized review, or validating least privilege after an RBAC change. Keywords: RBAC, ClusterRoleBinding, service account token, auth can-i, rbac-police, escalate, bind, impersonate. Do not use for designing and applying hardened RBAC - use implementing-rbac-hardening-for-kubernetes.

- **Kind:** skill
- **Source:** https://github.com/mukul975/Anthropic-Cybersecurity-Skills
- **Page:** https://forefy.com/skills/a3f71679-25aa-4dcf-8cda-1ffd32c14cdf
- **API (JSON + files):** https://forefy.com/api/asr/a3f71679-25aa-4dcf-8cda-1ffd32c14cdf

---

## LICENSE

```

```

## SKILL.md

---
name: auditing-kubernetes-rbac-privilege-escalation
description: >-
  Finds over-permissive RBAC roles and service-account token abuse paths in a Kubernetes
  cluster using kubectl auth can-i, rbac-police, kubectl-who-can, and rakkess, tracing which
  subjects can escalate toward cluster-admin. Use when reviewing who can escalate privileges
  in a cluster, hunting exploitable RoleBindings during an authorized review, or validating
  least privilege after an RBAC change. Keywords: RBAC, ClusterRoleBinding, service account
  token, auth can-i, rbac-police, escalate, bind, impersonate. Do not use for designing and
  applying hardened RBAC - use implementing-rbac-hardening-for-kubernetes.
domain: cybersecurity
subdomain: container-security
tags:
- kubernetes
- rbac
- privilege-escalation
- service-account
- least-privilege
- kubectl
- access-control
- attack-paths
version: '1.0'
author: mahipal
license: Apache-2.0
nist_csf:
- PR.AA-05
mitre_attack:
- T1078
---
# Auditing Kubernetes RBAC Privilege Escalation

> **Legal 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:

- **`escalate` on roles** — grant yourself any permission, even ones you do not hold.
- **`bind` on clusterroles** — create a binding to `cluster-admin`.
- **`impersonate`** on users/groups/serviceaccounts — act as any subject including `system:masters`.
- **`create`/`update`/`patch` on `pods`** — schedule a privileged pod or mount the node, escaping to the host (T1611).
- **`create` on `pods/exec`, `pods/attach`, `pods/ephemeralcontainers`** — run code in any existing pod.
- **`get`/`list`/`watch` on `secrets`** — list returns full secret contents, including other service-account tokens.
- **`create` on `serviceaccounts/token`** — mint tokens for more privileged accounts.
- **`update`/`patch` on `validatingwebhookconfigurations`/`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

- `kubectl` configured 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:
  ```bash
  # 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`, and `ClusterRoleBinding` objects
- 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

```bash
# 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).

```bash
# 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:builder
```

### Step 3: Hunt the Escalation Primitives

```bash
# 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.yaml
```

### Step 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.

```bash
# 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.json
```

### Step 5: Trace Pods to Over-Privileged Service Accounts

A finding only matters if a reachable workload mounts that token.

```bash
# 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 serviceaccount
```

### Step 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.

```bash
# 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 escalation
```

### Step 7: Report and Remediate

```bash
# 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

```

```

## references/api-reference.md

# 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/

## references/standards.md

# 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

```

```

## scripts/agent.py

```python
#!/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())
```

