container security

Performing Container Escape Detection

Detects container escape attempts by analyzing namespace configurations, privileged container checks, dangerous capability assignments, and host path mounts using the kubernetes Python client. Identifies CVE-2022-0492 style escapes via cgroup abuse. Use when auditing container security posture or investigating escape attempts.

container-escapecontainer-securitylinux-capabilitiesnamespace-analysisprivileged-containerthreat-detection
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • When conducting security assessments that involve performing container escape detection
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Familiarity with container security concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Instructions

Audit Kubernetes pods for container escape vectors including privileged mode, dangerous capabilities, host namespace sharing, and writable hostPath mounts.

from kubernetes import client, config
config.load_kube_config()
v1 = client.CoreV1Api()
 
pods = v1.list_pod_for_all_namespaces()
for pod in pods.items:
    for container in pod.spec.containers:
        sc = container.security_context
        if sc and sc.privileged:
            print(f"PRIVILEGED: {pod.metadata.namespace}/{pod.metadata.name}")

Key escape vectors:

  1. Privileged containers (full host access)
  2. CAP_SYS_ADMIN capability
  3. Host PID/Network/IPC namespace sharing
  4. Writable hostPath mounts to / or /etc
  5. Docker socket mount (/var/run/docker.sock)

Examples

# Check for docker socket mounts
for vol in pod.spec.volumes or []:
    if vol.host_path and "docker.sock" in (vol.host_path.path or ""):
        print(f"Docker socket exposed: {pod.metadata.name}")
Source materials

References and resources

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

References 1

api-reference.md1.5 KB

API Reference: Performing Container Escape Detection

kubernetes Python Client

from kubernetes import client, config
 
config.load_kube_config()  # or config.load_incluster_config()
v1 = client.CoreV1Api()
 
pods = v1.list_pod_for_all_namespaces()
for pod in pods.items:
    spec = pod.spec
    # Check host namespace sharing
    print(spec.host_pid, spec.host_network, spec.host_ipc)
    for c in spec.containers:
        sc = c.security_context
        if sc:
            print(sc.privileged, sc.capabilities, sc.run_as_user)
    for vol in spec.volumes or []:
        if vol.host_path:
            print(vol.host_path.path)

Container Escape Vectors

Vector Field Severity
Privileged mode securityContext.privileged CRITICAL
SYS_ADMIN cap capabilities.add CRITICAL
Docker socket hostPath: /var/run/docker.sock CRITICAL
Host PID ns hostPID: true HIGH
Host Network hostNetwork: true HIGH
Writable / mount hostPath: / CRITICAL
Run as root runAsUser: 0 MEDIUM

Dangerous Linux Capabilities

SYS_ADMIN, SYS_PTRACE, SYS_RAWIO, SYS_MODULE,
DAC_READ_SEARCH, NET_ADMIN, NET_RAW

References

Scripts 1

agent.py6.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for detecting container escape vectors in Kubernetes."""

import json
import argparse
from datetime import datetime

from kubernetes import client, config


DANGEROUS_CAPS = [
    "SYS_ADMIN", "SYS_PTRACE", "SYS_RAWIO", "SYS_MODULE",
    "DAC_READ_SEARCH", "NET_ADMIN", "NET_RAW",
]

DANGEROUS_HOST_PATHS = ["/", "/etc", "/root", "/var/run/docker.sock",
                        "/var/run/crio", "/proc", "/sys"]


def load_kube_config():
    """Load Kubernetes configuration."""
    try:
        config.load_incluster_config()
    except config.ConfigException:
        config.load_kube_config()


def check_privileged_containers(v1):
    """Find pods running privileged containers."""
    findings = []
    pods = v1.list_pod_for_all_namespaces()
    for pod in pods.items:
        for container in pod.spec.containers or []:
            sc = container.security_context
            if sc and sc.privileged:
                findings.append({
                    "namespace": pod.metadata.namespace,
                    "pod": pod.metadata.name,
                    "container": container.name,
                    "issue": "privileged container",
                    "severity": "CRITICAL",
                })
    return findings


def check_dangerous_capabilities(v1):
    """Find containers with dangerous Linux capabilities."""
    findings = []
    pods = v1.list_pod_for_all_namespaces()
    for pod in pods.items:
        for container in pod.spec.containers or []:
            sc = container.security_context
            if not sc or not sc.capabilities or not sc.capabilities.add:
                continue
            for cap in sc.capabilities.add:
                if cap in DANGEROUS_CAPS:
                    findings.append({
                        "namespace": pod.metadata.namespace,
                        "pod": pod.metadata.name,
                        "container": container.name,
                        "capability": cap,
                        "severity": "HIGH",
                    })
    return findings


def check_host_namespaces(v1):
    """Find pods sharing host PID, network, or IPC namespaces."""
    findings = []
    pods = v1.list_pod_for_all_namespaces()
    for pod in pods.items:
        spec = pod.spec
        ns_issues = []
        if spec.host_pid:
            ns_issues.append("hostPID")
        if spec.host_network:
            ns_issues.append("hostNetwork")
        if spec.host_ipc:
            ns_issues.append("hostIPC")
        if ns_issues:
            findings.append({
                "namespace": pod.metadata.namespace,
                "pod": pod.metadata.name,
                "host_namespaces": ns_issues,
                "severity": "HIGH",
            })
    return findings


def check_dangerous_mounts(v1):
    """Find pods with dangerous hostPath volume mounts."""
    findings = []
    pods = v1.list_pod_for_all_namespaces()
    for pod in pods.items:
        for vol in pod.spec.volumes or []:
            if not vol.host_path:
                continue
            path = vol.host_path.path
            if any(path == dp or path.startswith(dp + "/") for dp in DANGEROUS_HOST_PATHS):
                findings.append({
                    "namespace": pod.metadata.namespace,
                    "pod": pod.metadata.name,
                    "volume": vol.name,
                    "host_path": path,
                    "severity": "CRITICAL" if path in ("/", "/var/run/docker.sock") else "HIGH",
                })
    return findings


def check_docker_socket(v1):
    """Specifically detect Docker/CRI socket mounts."""
    findings = []
    sockets = ["/var/run/docker.sock", "/var/run/crio/crio.sock",
               "/run/containerd/containerd.sock"]
    pods = v1.list_pod_for_all_namespaces()
    for pod in pods.items:
        for vol in pod.spec.volumes or []:
            if vol.host_path and vol.host_path.path in sockets:
                findings.append({
                    "namespace": pod.metadata.namespace,
                    "pod": pod.metadata.name,
                    "socket_path": vol.host_path.path,
                    "severity": "CRITICAL",
                })
    return findings


def check_root_containers(v1):
    """Find containers running as root."""
    findings = []
    pods = v1.list_pod_for_all_namespaces()
    for pod in pods.items:
        for container in pod.spec.containers or []:
            sc = container.security_context
            if sc and sc.run_as_user == 0:
                findings.append({
                    "namespace": pod.metadata.namespace,
                    "pod": pod.metadata.name,
                    "container": container.name,
                    "issue": "running as UID 0",
                    "severity": "MEDIUM",
                })
            elif not sc or sc.run_as_non_root is not True:
                findings.append({
                    "namespace": pod.metadata.namespace,
                    "pod": pod.metadata.name,
                    "container": container.name,
                    "issue": "runAsNonRoot not enforced",
                    "severity": "LOW",
                })
    return findings


def main():
    parser = argparse.ArgumentParser(description="Container Escape Detection Agent")
    parser.add_argument("--output", default="container_escape_report.json")
    parser.add_argument("--action", choices=[
        "privileged", "capabilities", "namespaces", "mounts", "socket", "full_scan"
    ], default="full_scan")
    args = parser.parse_args()

    load_kube_config()
    v1 = client.CoreV1Api()
    report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}}

    if args.action in ("privileged", "full_scan"):
        findings = check_privileged_containers(v1)
        report["findings"]["privileged"] = findings
        print(f"[+] Privileged containers: {len(findings)}")

    if args.action in ("capabilities", "full_scan"):
        findings = check_dangerous_capabilities(v1)
        report["findings"]["dangerous_caps"] = findings
        print(f"[+] Dangerous capabilities: {len(findings)}")

    if args.action in ("namespaces", "full_scan"):
        findings = check_host_namespaces(v1)
        report["findings"]["host_namespaces"] = findings
        print(f"[+] Host namespace sharing: {len(findings)}")

    if args.action in ("mounts", "full_scan"):
        findings = check_dangerous_mounts(v1)
        report["findings"]["dangerous_mounts"] = findings
        print(f"[+] Dangerous mounts: {len(findings)}")

    if args.action in ("socket", "full_scan"):
        findings = check_docker_socket(v1)
        report["findings"]["socket_mounts"] = findings
        print(f"[+] Container runtime socket mounts: {len(findings)}")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
Keep exploring