Platform · Jul 24, 2026 · 7 min read

Container escape via config: the boundary the Dockerfile cannot enforce

A privileged container with a host-path mount is not isolated - it is a chroot with networking. The config that disables the boundary is the finding, not the CVE that exploits it.

Container isolation is a runtime property enforced by the orchestrator manifest, not by the image. The Dockerfile defines what process runs inside a container; the pod spec or task definition defines what that process can see, signal, mount, and bind. A single misconfigured field - privileged: true, a host-path volume at /, hostPID enabled - collapses the Linux namespaces and capabilities the entire container model depends on. The misconfiguration is the finding. No CVE is needed to cross the boundary.

01The isolation stack the manifest can disable

Linux containers are not virtual machines. The process boundary rests on three kernel mechanisms: namespaces (pid, net, mnt, ipc, uts) that control visibility into host resources, cgroups that constrain resource consumption, and Linux Security Modules - seccomp, AppArmor, SELinux, and the capability set - that filter syscalls and privilege. Every container runtime enables these by default. Every orchestrator lets the manifest disable them explicitly, one field at a time or all at once.

HOST KERNEL pid namespace hostPID: true ──► bypassed (see / signal all node processes) network namespace hostNetwork: true ──► bypassed (bind node ports, sniff pod traffic) mount namespace hostPath: / ──► bypassed (read / write entire node filesystem) capabilities CAP_SYS_ADMIN ──► granted (mount, nsenter, unshare to host ns) seccomp profile Unconfined ──► disabled (any syscall permitted)
Each row is a separate enforcement layer. A single manifest field can disable any of them; privileged: true disables all five at once.

02Six fields that open an escape path

privileged: true disables all Linux security mechanisms in a single field. The container process is equivalent to root on the host. Docker's --privileged flag and Kubernetes' securityContext.privileged both invoke this path. CAP_SYS_ADMIN is a close second: it grants the ability to mount arbitrary filesystems, call unshare, and nsenter directly into host namespaces. Several documented escape chains require nothing beyond this one capability. A host-path volume mounting /, /etc, or /var/run/docker.sock with write access lets a root-inside-container process modify node cron entries, SSH authorized keys, or issue Docker API calls that spawn a fresh privileged container. hostPID: true shares the host PID namespace - a root process in the container can ptrace or signal every process on the node, including kubelet. hostNetwork: true shares the host network stack, allowing a container to bind node-level ports and sniff inter-pod traffic. An absent or Unconfined seccomp profile does not create an escape on its own but widens the syscall surface that the chains above depend on.

pod-vulnerable.yamlyaml
# dangerous: privileged + host-path mount = node takeover path
apiVersion: v1
kind: Pod
spec:
  hostPID: true          # shares host PID namespace
  hostNetwork: true      # shares host network stack
  containers:
  - name: app
    image: example-app:v1.2
    securityContext:
      privileged: true    # disables all namespace / LSM isolation
      runAsUser: 0        # root inside container
    volumeMounts:
    - name: host-root
      mountPath: /host
  volumes:
  - name: host-root
    hostPath:
      path: /          # entire node filesystem, writable by root above

# corrected: drop capabilities, non-root, no host namespaces
spec:
  containers:
  - securityContext:
      allowPrivilegeEscalation: false
      runAsNonRoot: true
      runAsUser: 10001
      capabilities:
        drop: [ALL]
      seccompProfile:
        type: RuntimeDefault
  volumes: []            # no host-path volumes

03IaC parsing confirms the config as a fact

The confirming artifact is a parsed field in a manifest - the evidence is a file path, a line number, and the field value. No exploit is sent; no process attempts to cross the boundary during the scan. The parser reads Kubernetes YAML, docker-compose privileged: true, Terraform aws_ecs_task_definition containerDefinitions JSON, and Helm values files. Each source has a different field path but the same confirming logic: dangerous field present, evidence attached, policy violated.

Proven - CriticalPrivileged container with host-path mount - deployments/payments/pod-spec.yaml:31
securityContext.privileged: true + volumes[0].hostPath.path: / ──► root process inside container has full read/write access to node filesystem evidence: pod-spec.yaml line 31 (privileged), line 47 (hostPath)
Oracle: IaC parse. Combination of privileged and host-root volume on an internet-reachable workload; any code-execution path inside the container trivially escalates to node takeover. No exploit sent.
ConfigWhat it enablesSeverity
privileged: trueDisables all namespace and LSM isolation in one fieldCritical
CAP_SYS_ADMINMount arbitrary filesystems, nsenter to host namespace, unshareCritical
hostPath: / (writable, root)Write to node filesystem: cron, SSH keys, kubelet configCritical
hostPID: trueptrace and signal all node processes including kubeletHigh
hostNetwork: trueBind node-level ports, sniff inter-pod trafficHigh
seccomp: UnconfinedPermits raw syscalls that escape-chain gadgets rely onMedium

04Severity follows the confirmed combination

A disabled seccomp profile in isolation is a medium - it widens the syscall surface but does not hand over the host by itself. Privileged in isolation is critical because it already disables every enforcement layer regardless of what else is set. The dangerous combinations escalate a high to critical: hostPID: true with CAP_SYS_PTRACE is a process-injection path; a writable hostPath: / with runAsUser: 0 is a node-level file-write path that survives a container restart; hostNetwork: true with CAP_NET_ADMIN is traffic interception on the node's physical interfaces. Each combination carries a separate finding with the specific combination named, not a single "container misconfiguration" catch-all.

Remediation does not require pulling the workload. Most cases resolve to a security context addendum in the manifest: drop ALL capabilities and add back only what the process actually needs, set runAsNonRoot: true with an explicit non-zero UID, replace host-path volumes with named or ephemeral volumes, and pin the seccomp profile to RuntimeDefault or a custom allowlist. Each finding carries the manifest file path and line of the offending field alongside the corrected value - so the fix targets the real source, not a category label.

6
escape-enabling config classes tracked
0
exploits sent - IaC parse only
Critical
rating for privileged or host-root mount

The boundary the container model depends on is either present in the manifest or it is not. IaC parsing makes that a deterministic check - the same manifest field produces the same finding every run, with the file and line attached as proof. Severity you can defend means knowing the difference between a seccomp gap that widens an already-difficult surface and a privileged pod reachable by a low-privilege attacker on the same cluster who can use it as a node-level foothold. One is a cleanup item with a 30-day SLA; the other is a 7-day critical regardless of how internal the workload appears.

Evidence over heuristicsReachable beats foundMonitor → Block: rolling out a gate without slowing teamsIaC-grounded threat modelingAir-gapped AppSec with no phone-homeOne platform vs three tools
See it on your own app →