Like someone sneaking a prank sign onto your office door — annoying and visible, but no keys were stolen and the locks still work
CVE-2026-41579 is a symlink-following flaw (CWE-61) in runc's setupPtmx and setupDevSymlinks functions. When runc initializes a container's /dev directory, it calls os.Remove and os.Symlink using filepath.Join on unsanitized paths. A malicious container image can set /dev itself as a symlink pointing to an arbitrary *pre-existing* host directory. runc then blindly deletes a file called ptmx in that directory and creates a hardcoded set of symlinks — core, fd, ptmx, stdin, stdout, stderr — with fixed targets (/proc/kcore, /proc/self/fd, /dev/pts/ptmx, /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2). Affected versions: all runc prior to 1.3.6, all 1.4.x before 1.4.3, and all 1.5.0-rc.x before 1.5.0-rc.3. Docker is completely immune because it adds a read-only top layer that masks the malicious symlink. Podman and containerd (including the 95% of Kubernetes clusters using containerd as their CRI) do *not* add this layer and are vulnerable.
The vendor's LOW / 3.3 rating is honest and well-calibrated for a change. The impact ceiling is genuinely low: no confidentiality leak, no availability impact, no container escape, no privilege escalation, no arbitrary file write. The attacker cannot choose the symlink targets — they are hardcoded to standard /dev device paths. The only tangible outcome is dropping a handful of fixed symlinks into a pre-existing host directory or deleting a ptmx file that devpts will typically recreate. Even on a Kubernetes production node running containerd, exploiting this gives you graffiti, not a foothold. The broader containerd exposure justifies a slight nudge to 3.6, but this stays firmly in LOW territory.
4 steps from start to impact.
Craft malicious container image
/dev directory entry is replaced with a symbolic link pointing to a target directory on the host filesystem, e.g., /dev → /etc/some-dir. This image must be published to a registry accessible to the victim. No special tooling beyond standard image-building utilities (buildah, docker build with a modified layer) is needed.- Attacker can publish or inject an image into a registry the victim pulls from
- Target directory on the host must already exist (symlink will not create it)
- Most enterprises restrict image sources to trusted registries via admission controllers (OPA/Gatekeeper, Kyverno)
- Image signing (cosign/Notary) blocks unsigned or tampered images
- Registry vulnerability scanning (Trivy, Grype, Snyk Container) can flag anomalous
/devsymlinks in layers
/dev can flag this. No Nuclei template or dedicated scanner signature identified as of October 2026.Victim pulls and runs the image
- Victim uses Podman or containerd (Docker is immune)
- No admission controller blocks the image
- User or automation initiates
run/ Pod creation
- Docker users (dominant in dev environments) are completely unaffected
- Kubernetes admission controllers with image-allow-listing prevent unknown images
- User namespaces (rootless Podman, Kubernetes user namespace support) restrict writes to world-writable directories only
runc follows the /dev symlink during rootfs setup
setupPtmx calls os.Remove(filepath.Join(rootfs, "/dev/ptmx")) and then os.Symlink to create the ptmx device link. Because /dev is a symlink to a host directory, filepath.Join resolves to the host path. Similarly, setupDevSymlinks creates symlinks for core, fd, stdin, stdout, stderr in the resolved host directory. runc does not validate that /dev is a real directory rather than a symlink.- runc version is < 1.3.6, < 1.4.3, or < 1.5.0-rc.3
- Runtime is not Docker (no read-only top layer)
- SELinux in enforcing mode (default on RHEL/Fedora) constrains runc's write targets via
container_runtime_tpolicy - AppArmor profiles on Ubuntu restrict filesystem operations outside expected paths
Host directory modified with hardcoded symlinks
core → /proc/kcore, fd → /proc/self/fd, ptmx → /dev/pts/ptmx, stdin → /proc/self/fd/0, stdout → /proc/self/fd/1, stderr → /proc/self/fd/2) and may have had a file named ptmx deleted. These are standard /dev device paths — the attacker cannot control the symlink targets. The practical impact is host filesystem integrity violation with no path to code execution, data exfiltration, or privilege escalation.- Steps 1-3 succeeded
- Target directory is writable by the runc process
- The symlinks point to standard device paths — weaponizing them requires a separate, unrelated vulnerability in a host process that reads from the target directory
- Deleted
ptmxfiles are typically recreated by devpts mount - Impact is cosmetic / integrity-only — no confidentiality or availability effect
container_runtime_t policy constrains runc's filesystem operations. On Ubuntu/Debian, AppArmor profiles achieve the same. This may block the symlink creation or deletion at Step 3 depending on the target directory's label. Verify your policy covers the specific paths.864db8042dbb and 66acd48f9d42 convert the rootfs initialization code to fd-based operations that cannot follow symlinks. Per noisgate remediation SLA for LOW severity, patch as backlog hygiene — no hard deadline, but track and close within your regular update cycle.- Network segmentation / firewall rules — This is a local vulnerability triggered by running a container image, not a network-based attack. Firewalls have no effect.
- Runtime security tools (Falco, Sysdig) in default configuration — Default rulesets focus on container escape indicators (namespace manipulation, mount abuse, process injection). Creating standard
/devsymlinks in a host directory is unlikely to trigger default rules without custom policies. - Read-only container filesystem (
--read-onlyflag) — The vulnerable code runs *before* the container filesystem is mounted read-only. The symlink operations happen during rootfs setup, not at container runtime.
The supporting signals.
| In-the-Wild Exploitation | None observed. Not listed in CISA KEV. No campaigns, threat actor activity, or honeypot hits reported by GreyNoise or other CTI sources as of October 2026. |
|---|---|
| Proof-of-Concept Availability | No public PoC identified. Checked pocindex.io, GitHub (no repos named CVE-2026-41579), ExploitDB, and Nuclei templates. The advisory describes the mechanism in detail, making a PoC trivial to construct with buildah or manual layer manipulation, but none has been published. |
| EPSS Score | 0.00186 (bottom ~15th percentile) — reflects near-zero predicted exploitation probability in the next 30 days. |
| CISA KEV Status | Not listed. No federal mandate to patch on an accelerated timeline. |
| CVSS Vector | CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N — Local access, low complexity, no privileges required but user interaction needed. Scope unchanged. Integrity-only impact (Low). No confidentiality or availability impact. |
| Affected Versions | runc < 1.3.6, runc 1.4.0 through 1.4.2, runc 1.5.0-rc.1 through 1.5.0-rc.2. Affects Podman and containerd runtimes; Docker is immune (read-only top layer). |
| Fixed Versions | runc 1.3.6, 1.4.3, 1.5.0-rc.3. Debian unstable: 1.3.6+ds1-1. Debian bookworm/trixie: still vulnerable as of October 2026. Patches: commits 864db8042dbb and 66acd48f9d42. |
| Exposure / Installed Base | runc underpins ~95% of Kubernetes clusters (via containerd) and all Podman installations. However, Docker's immunity covers the majority of developer workstations. Kubernetes production nodes are the primary exposed population. Not remotely exploitable — no Shodan/Censys exposure relevant. |
| Disclosure Timeline | oss-security disclosure: 2026-06-13. CVE published: 2026-07-01. Patches available at disclosure. |
| Credited Researchers | Davias (initial finder), Arthur Chan, Junyi Liu, Derek Manzella. |
Sources.
Why this verdict
- Impact ceiling is genuinely negligible. The attacker can only create six hardcoded symlinks with fixed targets (standard
/devdevice paths) or delete a file namedptmx. No confidentiality leak, no availability disruption, no code execution, no container escape. This is filesystem graffiti, not a compromise. - User interaction required narrows the attack surface. A victim must explicitly pull and run the malicious image (UI:R). In enterprises with admission controllers, image signing, and registry allow-listing, this is a significant gate.
- Docker immunity eliminates the largest runtime population. Docker's read-only top layer completely blocks the attack. Only Podman and containerd users are exposed.
- Role multiplier: Kubernetes production nodes (containerd). The chain *does* succeed on Kubernetes nodes running containerd — and containerd is the default CRI for ~95% of K8s clusters. However, even on a production Kubernetes node, the blast radius is *host-local integrity violation only*: six hardcoded symlinks in one directory. This does NOT produce domain takeover, fleet compromise, mass data egress, or supply-chain pivot. The outcome is the same LOW-impact filesystem modification regardless of the host's role. The high-value-role floor does not trigger because the impact category (trivial integrity violation with fixed targets) does not match the floor-triggering outcomes.
- Role multiplier: CI/CD workers (containerd/Podman). Same analysis — the chain can land on a CI runner, but creating
/proc/kcoresymlinks in a host directory doesn't give supply-chain write access or artifact tampering capability. No floor trigger. - Slight upward adjustment from 3.3 → 3.6 reflects that containerd's 95% Kubernetes share makes the reachable population larger than a Docker-centric framing implies, even though the impact remains minimal.
Why not higher?
The impact ceiling locks this in LOW. Even if every friction point is removed and the chain succeeds on the highest-value Kubernetes production node, the attacker gains the ability to place six *hardcoded* symlinks with *fixed, non-weaponizable* targets in a single pre-existing directory. There is no path from this primitive to container escape, RCE, privilege escalation, or data access without a separate, unrelated vulnerability. No PoC exists, EPSS is bottom-percentile, and no threat actor has shown interest.
Why not lower?
Containerd's dominance in Kubernetes means the reachable population is large — nearly every K8s node in production runs an affected runtime. The vulnerability is real and exploitable (not theoretical), the attack is low-complexity, and user namespaces / LSMs are not universally deployed. Dropping to IGNORE would understate the breadth of exposure, even if the depth of impact is shallow. It still warrants tracking and patching within the standard remediation window.
Crowdsourced verification payload.
Run this script on each container host (Kubernetes node, Podman host) as any user with read access to the runc binary. Example: bash check_cve_2026_41579.sh. No root required.
#!/usr/bin/env bash
# CVE-2026-41579 Checker — runc /dev symlink integrity violation
# Run on container hosts. No root required.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
RUNC_BIN=""
for candidate in runc /usr/bin/runc /usr/sbin/runc /usr/local/bin/runc /usr/local/sbin/runc; do
if command -v "$candidate" &>/dev/null 2>&1; then
RUNC_BIN="$candidate"
break
fi
done
if [ -z "$RUNC_BIN" ]; then
echo "UNKNOWN — runc binary not found on this host."
exit 2
fi
VERSION_OUTPUT=$("$RUNC_BIN" --version 2>/dev/null || true)
VERSION=$(echo "$VERSION_OUTPUT" | head -1 | grep -oP '[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?' || true)
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse runc version from: $VERSION_OUTPUT"
exit 2
fi
echo "Detected runc version: $VERSION"
# Check if Docker is the sole higher-level runtime (Docker is immune)
DOCKER_ONLY=false
if command -v docker &>/dev/null && ! command -v podman &>/dev/null; then
# Check if containerd is used outside Docker context
if ! systemctl is-active --quiet containerd 2>/dev/null || \
(docker info 2>/dev/null | grep -qi 'containerd'); then
DOCKER_ONLY=true
fi
fi
if $DOCKER_ONLY; then
echo "NOTE: Docker detected as sole runtime. Docker is IMMUNE to CVE-2026-41579"
echo " (read-only top layer masks malicious /dev symlinks)."
echo "PATCHED — Docker's architecture mitigates this vulnerability."
exit 0
fi
# Version comparison function
ver_to_num() {
local IFS=.
local parts=($1)
printf '%d%03d%03d' "${parts[0]:-0}" "${parts[1]:-0}" "${parts[2]:-0}"
}
MAJMINPATCH=$(echo "$VERSION" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')
RC_NUM=$(echo "$VERSION" | grep -oP 'rc\.\K[0-9]+' || echo "")
VER_NUM=$(ver_to_num "$MAJMINPATCH")
VULNERABLE=false
# Branch 1.3.x: fixed in 1.3.6
if [ "$VER_NUM" -lt $(ver_to_num "1.3.6") ]; then
VULNERABLE=true
# Branch 1.4.x: fixed in 1.4.3
elif [ "$VER_NUM" -ge $(ver_to_num "1.4.0") ] && [ "$VER_NUM" -lt $(ver_to_num "1.4.3") ]; then
VULNERABLE=true
# Branch 1.5.0-rc.x: fixed in 1.5.0-rc.3
elif [ "$MAJMINPATCH" = "1.5.0" ] && [ -n "$RC_NUM" ] && [ "$RC_NUM" -lt 3 ]; then
VULNERABLE=true
fi
RUNTIMES=""
command -v podman &>/dev/null && RUNTIMES="${RUNTIMES}podman "
pgrep -x containerd &>/dev/null && RUNTIMES="${RUNTIMES}containerd "
[ -z "$RUNTIMES" ] && RUNTIMES="unknown "
if $VULNERABLE; then
echo "VULNERABLE — runc $VERSION is affected by CVE-2026-41579."
echo " Exposed runtimes: $RUNTIMES"
echo " Fix: upgrade to runc 1.3.6, 1.4.3, or 1.5.0-rc.3+"
exit 1
else
echo "PATCHED — runc $VERSION is not affected by CVE-2026-41579."
exit 0
fi