container security

Detecting Privilege Escalation in Kubernetes Pods

Detect and prevent privilege escalation in Kubernetes pods by monitoring security contexts, capabilities, and syscall patterns with Falco and OPA policies.

capabilitiesdetectionkubernetespod-securityprivilege-escalationsecurity-context
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Privilege escalation in Kubernetes occurs when a pod or container gains elevated permissions beyond its intended scope. This includes running as root, using privileged mode, mounting host filesystems, enabling dangerous Linux capabilities, or exploiting kernel vulnerabilities. Detection combines admission control (prevention), runtime monitoring (detection), and audit logging (investigation).

When to Use

  • When investigating security incidents that require detecting privilege escalation in kubernetes pods
  • When building detection rules or threat hunting queries for this domain
  • When SOC analysts need structured procedures for this analysis type
  • When validating security monitoring coverage for related attack techniques

Prerequisites

  • Kubernetes cluster v1.25+ (Pod Security Admission support)
  • kubectl with cluster-admin access
  • Falco or similar runtime security tool
  • OPA Gatekeeper or Kyverno for admission policies

Privilege Escalation Vectors in Kubernetes

Vector Risk Detection Method
privileged: true Full host access Admission control + audit
hostPID: true Access host processes Admission control
hostNetwork: true Access host network stack Admission control
hostPath volumes Read/write host filesystem Admission control
SYS_ADMIN capability Near-privileged access Admission + runtime
allowPrivilegeEscalation: true setuid/setgid exploitation Admission control
runAsUser: 0 Container root Admission control
automountServiceAccountToken Token theft for API access Admission control
Writable /proc or /sys Kernel parameter manipulation Runtime monitoring

Detection with Admission Control

Pod Security Admission (Built-in)

# Enforce restricted policy on namespace
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

OPA Gatekeeper Policies

# Block dangerous capabilities
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sdangerouspriv
spec:
  crd:
    spec:
      names:
        kind: K8sDangerousPriv
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdangerouspriv
 
        dangerous_caps := {"SYS_ADMIN", "SYS_PTRACE", "SYS_MODULE", "DAC_OVERRIDE", "NET_ADMIN", "NET_RAW"}
 
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          cap := container.securityContext.capabilities.add[_]
          dangerous_caps[cap]
          msg := sprintf("Container %v adds dangerous capability: %v", [container.name, cap])
        }
 
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          container.securityContext.privileged == true
          msg := sprintf("Container %v runs in privileged mode", [container.name])
        }
 
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          container.securityContext.allowPrivilegeEscalation == true
          msg := sprintf("Container %v allows privilege escalation", [container.name])
        }
 
        violation[{"msg": msg}] {
          input.review.object.spec.hostPID == true
          msg := "Pod uses host PID namespace"
        }
 
        violation[{"msg": msg}] {
          input.review.object.spec.hostNetwork == true
          msg := "Pod uses host network"
        }

Runtime Detection with Falco

# /etc/falco/rules.d/privesc-detection.yaml
- rule: Setuid Binary Execution in Container
  desc: Detect execution of setuid/setgid binaries in a container
  condition: >
    spawned_process and container and
    (proc.name in (su, sudo, newgrp, chsh, passwd) or
     proc.is_exe_upper_layer=true)
  output: >
    Setuid/setgid binary executed in container
    (user=%user.name container=%container.name image=%container.image.repository
     command=%proc.cmdline parent=%proc.pname)
  priority: WARNING
  tags: [container, privilege-escalation, T1548]
 
- rule: Capability Gained in Container
  desc: Detect when a process gains elevated capabilities
  condition: >
    evt.type = capset and container and
    evt.arg.cap != ""
  output: >
    Process gained capabilities in container
    (container=%container.name image=%container.image.repository
     capabilities=%evt.arg.cap command=%proc.cmdline)
  priority: WARNING
  tags: [container, privilege-escalation, T1548.001]
 
- rule: Container with Dangerous Capabilities Started
  desc: Detect container launched with dangerous capabilities
  condition: >
    container_started and container and
    (container.image.repository != "registry.k8s.io/pause") and
    (container.cap_effective contains SYS_ADMIN or
     container.cap_effective contains SYS_PTRACE or
     container.cap_effective contains SYS_MODULE)
  output: >
    Container with dangerous capabilities
    (container=%container.name image=%container.image.repository
     caps=%container.cap_effective)
  priority: CRITICAL
  tags: [container, privilege-escalation, T1068]
 
- rule: Write to /etc/passwd in Container
  desc: Detect writes to /etc/passwd inside container
  condition: >
    open_write and container and fd.name = /etc/passwd
  output: >
    Write to /etc/passwd in container
    (container=%container.name image=%container.image.repository
     command=%proc.cmdline user=%user.name)
  priority: CRITICAL
  tags: [container, privilege-escalation, T1136]

Kubernetes Audit Log Detection

# audit-policy.yaml - Capture privilege escalation events
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # Log pod creation with security context details
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["pods"]
    verbs: ["create", "update", "patch"]
 
  # Log privilege escalation attempts
  - level: RequestResponse
    resources:
      - group: "rbac.authorization.k8s.io"
        resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"]
    verbs: ["create", "update", "patch", "bind", "escalate"]
 
  # Log service account token requests
  - level: Metadata
    resources:
      - group: ""
        resources: ["serviceaccounts/token"]
    verbs: ["create"]

Query Audit Logs for Privilege Escalation

# Find pods created with privileged security context
kubectl logs -n kube-system kube-apiserver-* | \
  jq 'select(.verb == "create" and .objectRef.resource == "pods") |
  select(.requestObject.spec.containers[].securityContext.privileged == true)'
 
# Find RBAC escalation attempts
kubectl logs -n kube-system kube-apiserver-* | \
  jq 'select(.objectRef.resource == "clusterrolebindings" and .verb == "create")'

Investigation Playbook

# Check pod security context
kubectl get pod <pod-name> -n <ns> -o jsonpath='{.spec.containers[*].securityContext}'
 
# Check effective capabilities
kubectl exec <pod-name> -n <ns> -- cat /proc/1/status | grep -i cap
 
# List pods running as root
kubectl get pods --all-namespaces -o json | \
  jq '.items[] | select(.spec.containers[].securityContext.runAsUser == 0 or .spec.containers[].securityContext.privileged == true) | {name: .metadata.name, ns: .metadata.namespace}'
 
# Check for hostPath volumes
kubectl get pods --all-namespaces -o json | \
  jq '.items[] | select(.spec.volumes[]?.hostPath != null) | {name: .metadata.name, ns: .metadata.namespace, paths: [.spec.volumes[].hostPath.path]}'

Best Practices

  1. Enable Pod Security Admission at restricted level for production namespaces
  2. Drop ALL capabilities and add back only what is needed
  3. Set allowPrivilegeEscalation: false on all containers
  4. Run as non-root (runAsNonRoot: true, runAsUser > 0)
  5. Disable automountServiceAccountToken unless API access is needed
  6. Monitor with Falco for runtime privilege escalation attempts
  7. Audit RBAC changes with Kubernetes audit logging
  8. Use seccomp profiles to restrict syscalls
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.2 KB

API Reference: Detecting Privilege Escalation in Kubernetes Pods

Security Context Checks

Check Risk Description
privileged: true CRITICAL Full host access
allowPrivilegeEscalation HIGH setuid escalation
runAsUser: 0 HIGH Running as root
hostPID: true CRITICAL Host PID namespace
hostNetwork: true HIGH Host network access

Dangerous Capabilities

Capability Risk
SYS_ADMIN Container escape
SYS_PTRACE Process debugging
SYS_MODULE Kernel module loading
NET_ADMIN Network manipulation

kubectl Audit Commands

kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged==true)'
kubectl auth can-i --list --as=system:serviceaccount:ns:sa

Pod Security Standards

apiVersion: v1
kind: Namespace
metadata:
  labels:
    pod-security.kubernetes.io/enforce: restricted

Falco Rules

- rule: Pod with Privileged Container
  condition: kevt and kcreate and container.privileged=true
  priority: CRITICAL

CLI Usage

python agent.py --namespace default
python agent.py --json-file pods.json
standards.md1.0 KB

Standards - Detecting Privilege Escalation in Kubernetes Pods

MITRE ATT&CK for Containers

Technique ID Description
Escape to Host T1611 Container breakout via privilege escalation
Exploitation for Privilege Escalation T1068 Kernel exploit from container
Abuse Elevation Control T1548 Setuid/setgid binary exploitation
Valid Accounts T1078 Service account token theft
Create Account T1136 Modify /etc/passwd in container

CIS Kubernetes Benchmark v1.8

  • 5.2.1-5.2.9: Pod Security Standards
  • 5.7.3: Apply security context to pods

NIST SP 800-190

  • Section 4.3: Container runtime vulnerabilities
  • Section 5.4: Runtime monitoring for privilege escalation

Pod Security Standards

Profile Level Key Restrictions
Privileged Unrestricted No restrictions
Baseline Minimally restrictive No privileged, no hostPID/hostNetwork
Restricted Heavily restricted Non-root, drop all caps, no privilege escalation
workflows.md1.2 KB

Workflow - Detecting Privilege Escalation in Kubernetes Pods

Phase 1: Assess Current State

# Find privileged pods
kubectl get pods -A -o json | jq '[.items[] | select(.spec.containers[].securityContext.privileged==true) | {name:.metadata.name, ns:.metadata.namespace}]'
 
# Find pods running as root
kubectl get pods -A -o json | jq '[.items[] | select(.spec.securityContext.runAsUser==0 or .spec.containers[].securityContext.runAsUser==0) | {name:.metadata.name, ns:.metadata.namespace}]'
 
# Find hostPath mounts
kubectl get pods -A -o json | jq '[.items[] | select(.spec.volumes[]?.hostPath!=null) | {name:.metadata.name, ns:.metadata.namespace}]'

Phase 2: Deploy Prevention

  1. Apply Pod Security Admission labels to namespaces
  2. Deploy OPA Gatekeeper constraints
  3. Test with non-compliant pods (should be rejected)

Phase 3: Deploy Detection

  1. Install Falco with privilege escalation rules
  2. Enable Kubernetes audit logging
  3. Configure alerts to SIEM

Phase 4: Respond to Alerts

  1. Identify compromised pod
  2. Check container security context
  3. Review process list and capabilities
  4. Isolate with network policy
  5. Capture forensic data
  6. Delete compromised pod

Scripts 2

agent.py4.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Kubernetes pod privilege escalation detection agent.

Audits pod security contexts, capabilities, and service account permissions
to detect misconfigurations enabling container privilege escalation.
"""

import argparse
import json
import subprocess
from datetime import datetime

DANGEROUS_CAPABILITIES = {
    "SYS_ADMIN", "SYS_PTRACE", "SYS_MODULE", "NET_ADMIN",
    "NET_RAW", "DAC_READ_SEARCH", "SYS_RAWIO",
}


def get_pods(namespace="--all-namespaces"):
    try:
        cmd = ["kubectl", "get", "pods", "-o", "json"]
        if namespace == "--all-namespaces":
            cmd.append("--all-namespaces")
        else:
            cmd.extend(["-n", namespace])
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode == 0:
            return json.loads(result.stdout).get("items", [])
    except (subprocess.TimeoutExpired, FileNotFoundError, json.JSONDecodeError):
        pass
    return []


def audit_pod(pod):
    findings = []
    pod_name = pod.get("metadata", {}).get("name", "")
    namespace = pod.get("metadata", {}).get("namespace", "")
    spec = pod.get("spec", {})

    if spec.get("hostPID"):
        findings.append({"check": "hostPID", "severity": "CRITICAL", "desc": "Host PID namespace"})
    if spec.get("hostNetwork"):
        findings.append({"check": "hostNetwork", "severity": "HIGH", "desc": "Host network"})
    if spec.get("hostIPC"):
        findings.append({"check": "hostIPC", "severity": "MEDIUM", "desc": "Host IPC"})

    for container in spec.get("containers", []) + spec.get("initContainers", []):
        ctx = container.get("securityContext", {})
        c_name = container.get("name", "")
        if ctx.get("privileged"):
            findings.append({"container": c_name, "check": "privileged",
                             "severity": "CRITICAL", "desc": "Privileged container"})
        if ctx.get("allowPrivilegeEscalation", True):
            findings.append({"container": c_name, "check": "allowPrivilegeEscalation",
                             "severity": "HIGH", "desc": "Privilege escalation allowed"})
        run_as = ctx.get("runAsUser")
        if run_as == 0 or (run_as is None and not ctx.get("runAsNonRoot")):
            findings.append({"container": c_name, "check": "runAsRoot",
                             "severity": "HIGH", "desc": "May run as root"})
        if not ctx.get("readOnlyRootFilesystem"):
            findings.append({"container": c_name, "check": "mutableFS",
                             "severity": "MEDIUM", "desc": "Writable root FS"})
        caps = ctx.get("capabilities", {})
        for cap in set(caps.get("add", [])) & DANGEROUS_CAPABILITIES:
            findings.append({"container": c_name, "check": "dangerous_cap",
                             "capability": cap, "severity": "CRITICAL"})
        for vm in container.get("volumeMounts", []):
            if vm.get("mountPath") in ("/var/run/docker.sock", "/run/containerd/containerd.sock"):
                findings.append({"container": c_name, "check": "runtime_socket",
                                 "severity": "CRITICAL", "desc": f"Socket: {vm['mountPath']}"})

    risk = "CRITICAL" if any(f["severity"] == "CRITICAL" for f in findings) else \
           "HIGH" if any(f["severity"] == "HIGH" for f in findings) else "MEDIUM"
    return {"pod": pod_name, "namespace": namespace, "findings": findings, "risk": risk}


def main():
    parser = argparse.ArgumentParser(description="K8s Pod PrivEsc Detector")
    parser.add_argument("--namespace", default="--all-namespaces")
    parser.add_argument("--json-file", help="Pod JSON file instead of kubectl")
    args = parser.parse_args()
    if args.json_file:
        with open(args.json_file) as f:
            data = json.load(f)
            pods = data.get("items", [data]) if "items" in data else [data]
    else:
        pods = get_pods(args.namespace)
    results = {"timestamp": datetime.utcnow().isoformat() + "Z", "pod_audits": []}
    for pod in pods:
        audit = audit_pod(pod)
        if audit["findings"]:
            results["pod_audits"].append(audit)
    results["total_pods_with_issues"] = len(results["pod_audits"])
    print(json.dumps(results, indent=2))


if __name__ == "__main__":
    main()
process.py5.3 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Kubernetes Privilege Escalation Scanner - Scan cluster for pods with
dangerous security configurations that enable privilege escalation.
"""

import json
import subprocess
import sys
import argparse


DANGEROUS_CAPS = {"SYS_ADMIN", "SYS_PTRACE", "SYS_MODULE", "DAC_OVERRIDE",
                  "NET_ADMIN", "NET_RAW", "SYS_RAWIO", "SYS_BOOT"}


def get_pods(namespace: str = None) -> list:
    """Get all pods from cluster."""
    cmd = ["kubectl", "get", "pods", "-o", "json"]
    if namespace:
        cmd.extend(["-n", namespace])
    else:
        cmd.append("--all-namespaces")
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"Error: {result.stderr}", file=sys.stderr)
        return []
    return json.loads(result.stdout).get("items", [])


def check_pod_privesc(pod: dict) -> list:
    """Check a pod for privilege escalation risks."""
    findings = []
    name = pod["metadata"]["name"]
    ns = pod["metadata"].get("namespace", "default")
    spec = pod.get("spec", {})

    # Host-level checks
    if spec.get("hostPID"):
        findings.append({"pod": name, "namespace": ns, "severity": "CRITICAL",
                        "finding": "hostPID enabled", "vector": "host-pid"})
    if spec.get("hostNetwork"):
        findings.append({"pod": name, "namespace": ns, "severity": "HIGH",
                        "finding": "hostNetwork enabled", "vector": "host-network"})
    if spec.get("hostIPC"):
        findings.append({"pod": name, "namespace": ns, "severity": "HIGH",
                        "finding": "hostIPC enabled", "vector": "host-ipc"})

    # Volume checks
    for vol in spec.get("volumes", []):
        if vol.get("hostPath"):
            path = vol["hostPath"].get("path", "")
            findings.append({"pod": name, "namespace": ns, "severity": "HIGH",
                           "finding": f"hostPath volume: {path}", "vector": "host-path"})

    # Container checks
    for container in spec.get("containers", []) + spec.get("initContainers", []):
        cname = container.get("name", "unknown")
        sc = container.get("securityContext", {})

        if sc.get("privileged"):
            findings.append({"pod": name, "namespace": ns, "severity": "CRITICAL",
                           "finding": f"Container '{cname}' is privileged", "vector": "privileged"})

        if sc.get("allowPrivilegeEscalation", True):
            findings.append({"pod": name, "namespace": ns, "severity": "MEDIUM",
                           "finding": f"Container '{cname}' allows privilege escalation",
                           "vector": "allow-privesc"})

        if sc.get("runAsUser") == 0:
            findings.append({"pod": name, "namespace": ns, "severity": "HIGH",
                           "finding": f"Container '{cname}' runs as root (UID 0)",
                           "vector": "run-as-root"})

        caps = sc.get("capabilities", {})
        for cap in caps.get("add", []):
            if cap in DANGEROUS_CAPS or cap == "ALL":
                findings.append({"pod": name, "namespace": ns, "severity": "CRITICAL",
                               "finding": f"Container '{cname}' has dangerous cap: {cap}",
                               "vector": "dangerous-capability"})

    return findings


def scan_cluster(namespace: str = None) -> list:
    """Scan entire cluster for privilege escalation risks."""
    pods = get_pods(namespace)
    all_findings = []
    for pod in pods:
        findings = check_pod_privesc(pod)
        all_findings.extend(findings)
    return all_findings


def print_report(findings: list):
    """Print scan report."""
    if not findings:
        print("No privilege escalation risks found.")
        return

    print(f"\n=== Kubernetes Privilege Escalation Scan ===")
    print(f"Total findings: {len(findings)}\n")

    severity_order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}
    findings.sort(key=lambda x: severity_order.get(x["severity"], 4))

    print(f"{'Severity':<10} {'Namespace':<20} {'Pod':<35} {'Finding'}")
    print("-" * 100)
    for f in findings:
        print(f"{f['severity']:<10} {f['namespace']:<20} {f['pod']:<35} {f['finding']}")

    # Summary
    from collections import Counter
    by_sev = Counter(f["severity"] for f in findings)
    print(f"\nSummary: CRITICAL={by_sev.get('CRITICAL',0)} HIGH={by_sev.get('HIGH',0)} "
          f"MEDIUM={by_sev.get('MEDIUM',0)} LOW={by_sev.get('LOW',0)}")


def main():
    parser = argparse.ArgumentParser(description="Kubernetes Privilege Escalation Scanner")
    parser.add_argument("--namespace", "-n", help="Scan specific namespace")
    parser.add_argument("--json", action="store_true", help="JSON output")
    parser.add_argument("--fail-on", choices=["critical", "high", "medium"],
                       help="Exit non-zero if findings at threshold")

    args = parser.parse_args()
    findings = scan_cluster(args.namespace)

    if args.json:
        print(json.dumps(findings, indent=2))
    else:
        print_report(findings)

    if args.fail_on:
        threshold = {"critical": ["CRITICAL"], "high": ["CRITICAL", "HIGH"],
                    "medium": ["CRITICAL", "HIGH", "MEDIUM"]}
        blocking = [f for f in findings if f["severity"] in threshold[args.fail_on]]
        sys.exit(1 if blocking else 0)


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.0 KB
Keep exploring