CVE-2026-41579: Verified Repro With Script Download
CVE-2026-41579: runc symlink deletion via malicious /dev symlink in container image
CVE-2026-41579 is verified against the affected target. This low reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00206.
What Is CVE-2026-41579?
CVE-2026-41579 is a low-severity symlink-following issue (CWE-61) in opencontainers/runc where a malicious container image can trick runc into deleting a host file and creating symlinks on the host during rootfs setup. Pruva reproduced it (reproduction REPRO-2026-00206).
CVE-2026-41579 Severity & CVSS Score
CVE-2026-41579 is rated low severity, with a CVSS base score of 3.3 out of 10.
Low — limited impact or hard to exploit. Address in the normal cycle.
How to Reproduce CVE-2026-41579
pruva-verify REPRO-2026-00206 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00206/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-41579
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
container image rootfs where /dev is a symlink to an attacker-controlled host directory
- runc run cve-ptmx-test -b /bundle
- rootfs /dev setup
- setupPtmx/setupDevSymlinks
CVE-2026-41579 can be triggered through runc create+runc start and through a relative /dev symlink in the container image, in addition to the originally reported runc run with an absolute /dev symlink. Both alternate paths reach the same pre-pivot path-based /dev setup sink and are blocked by the fd-based fix in runc…
How the agent worked
Root Cause and Exploit Chain for CVE-2026-41579
CVE-2026-41579 is a low-severity host filesystem integrity issue in opencontainers/runc. When runc prepares a container rootfs, the functions setupPtmx and setupDevSymlinks operate on path strings under the bundle rootfs before pivot_root(2) occurs. If the container image has /dev as a symlink that points outside the rootfs (for example to a host directory controlled by the attacker), filepath.Join(rootfs, "/dev/ptmx") resolves through the symlink and runc deletes or re-creates files on the host. A malicious image can therefore trick runc into removing an existing file named ptmx and creating a small fixed set of device symlinks in an attacker-chosen host directory.
- Package / component: opencontainers/runc
- Affected versions: prior to 1.3.6, 1.4.0-rc.1 through 1.4.3, and 1.5.0-rc.1 through 1.5.0-rc.3
- Risk level: low (per upstream advisory)
- Consequences: Arbitrary deletion of a host file named
ptmxand creation of a limited set of hardcoded symlinks in a host directory reachable via a malicious/devsymlink. Not exploitable under Docker, but exploitable via other runc-based runtimes that do not mask/devwith a top-level read-only layer.
Impact Parity
- Disclosed / claimed maximum impact: Arbitrary file deletion and symlink creation on the host filesystem through a malicious container image (
/devsymlink). - Reproduced impact from this run: Vulnerable runc deleted a decoy file named
ptmxand replaced it with a symlink in an attacker-controlled directory; fixed runc left the decoy untouched. - Parity:
fullfor the documented filesystem-integrity impact. The reproduction does not demonstrate privilege escalation or code execution, which is consistent with the advisory's low-severity rating.
Root Cause
The bug is in runc's rootfs preparation code. Before the container pivots into its rootfs, setupPtmx and setupDevSymlinks use filepath.Join(rootfs, "/dev/...") and then call os.Remove / os.Symlink. Because the operations happen before pivot_root, a /dev entry in the image that is a symlink to an attacker-controlled host directory is followed, causing the operations to affect the host path instead of the container rootfs.
Upstream fix commit:
opencontainers/runc@864db8042dbb— "rootfs: make /dev initialisation code fd-based"
The fix rewrites the /dev setup code to operate on file descriptors relative to the opened rootfs directory, so symlinks in the image cannot redirect the operations to host paths.
Reproduction Steps
The reproduction is implemented in bundle/repro/reproduction_steps.sh. At a high level it:
- Verifies Docker is available.
- Downloads the vulnerable runc release binary (
v1.3.5) and the fixed release binary (v1.3.6). - Builds a minimal OCI rootfs from the official
busyboximage. - Builds two privileged Docker images (
repro-runc-vulnandrepro-runc-fixed) that each contain one runc binary and the rootfs. - Inside a privileged container, replaces
/bundle/rootfs/devwith a symlink to/controlled_devand creates a decoy/controlled_dev/ptmx. - Generates an OCI bundle with
runc spec, disables the terminal, and sets the command to/bin/true. - Runs
runc run cve-ptmx-test -b /bundle. - Checks whether the decoy file was deleted.
Expected evidence:
- Vulnerable (1.3.5): the
ptmxdecoy is removed and/controlled_devcontains symlinks such asptmx -> pts/ptmx,core -> /proc/kcore,fd -> /proc/self/fd, etc. - Fixed (1.3.6): the
ptmxdecoy remains untouched and runc does not create host symlinks.
Evidence
bundle/logs/repro_vuln.log— vulnerable runc 1.3.5 deletes the decoy and creates host symlinks.bundle/logs/repro_fixed.log— fixed runc 1.3.6 preserves the decoy.bundle/logs/build_repro-runc-vuln.log— Docker build log for the vulnerable image.bundle/logs/build_repro-runc-fixed.log— Docker build log for the fixed image.bundle/repro/runtime_manifest.json— runtime evidence manifest produced by the script.
Key excerpts:
Vulnerable run:
RUN_VERSION: runc version 1.3.5
BEFORE: /controlled_dev/ptmx present?
-rw-r---- 1 root root 10 ... ptmx
...
AFTER: /controlled_dev contents:
-rw-r--r-- ... ptmx
RESULT: decoy deleted
Fixed run:
RUN_VERSION: runc version 1.3.6
BEFORE: /controlled_dev/ptmx present?
-rw-r--r-- ... ptmx
...
AFTER: /controlled_dev contents:
-rw-r--r-- ... ptmx
RESULT: decoy preserved
Recommendations / Next Steps
- Upgrade runc to a patched version: 1.3.6, 1.4.3, or 1.5.0 (or later).
- Higher-level runtimes that consume runc should ensure container images cannot ship a
/devsymlink that resolves to a host path, or rely on the patched runc version. - Regression tests should include a rootfs where
/devis a symlink to a controlled host directory and verify thatsetupPtmx/setupDevSymlinksdo not operate on the host path.
Additional Notes
- The script is idempotent: it re-downloads only missing binaries, rebuilds the Docker images each run, and uses unique container names.
- The reproduction uses the real
runcCLI binary and the real OCI bundle execution path (runc run), not a reimplemented parser or mocked environment. - The Docker-in-Docker privileged container is required in this sandbox because the host environment lacks
CAP_SYS_ADMINand a writable cgroup hierarchy; inside the privileged container runc has the capabilities needed to create a genuine container. - No sanitizer or crash is involved; the proof relies on the filesystem state difference between the vulnerable and fixed versions.
Variant Analysis & Alternative Triggers for CVE-2026-41579
CVE-2026-41579 is a low-severity host filesystem integrity issue in opencontainers/runc. The original reproduction used an absolute /dev symlink in the container image to trick runc into deleting and recreating files on the host before pivot_root(2). This variant stage tested whether the same underlying bug could be triggered through a relative /dev symlink or through the runc create + runc start CLI entry point. Both alternate triggers reproduced the same host-side impact on the vulnerable version (runc 1.3.5), but neither bypassed the fixed version (runc 1.3.6). The upstream fd-based /dev setup also blocks a third candidate, a /dev/pts symlink, because the vulnerable code only touches /dev/ptmx, not /dev/pts itself. No true bypass of the patch was found.
Fix Coverage / Assumptions
The upstream fix (commit a8e53f2c in release-1.3, cherry-picked from 864db8042dbb) rewrites the pre-pivot /dev setup to operate on a pre-opened file descriptor for the real rootfs directory (rootFd). The key assumptions are:
rootFdis opened directly onconfig.RootfswithO_DIRECTORY|O_CLOEXEC|O_PATH, so it cannot be a symlink.- The
internal/pathrshelpers (UnlinkInRoot,SymlinkInRoot,MkdirAllParentInRoot) usepathrs-lite/filepath-securejointo walk paths relative torootFdwithout following symlinks that escape the rootfs. - All three setup phases (
createDevices,setupPtmx,setupDevSymlinks) are now funneled through a singledoSetupDev(rootFd, config)call. - The same merge also hardens the cgroupv1 merged-subsystem symlink creation (
9432ad3a) withpathrs.SymlinkInRoot, covering a sibling path-based symlink operation.
The fix does not change post-pivot_root behavior or other runtime-configured mount paths; it specifically closes the image-controlled /dev symlink path before the container switches root.
Variant / Alternate Trigger
Three distinct candidates were tested:
Relative
/devsymlink (/bundle/rootfs/dev -> ../controlled_dev).- A different data path from the original absolute symlink. The symlink still resolves to a directory outside the rootfs, so it reaches the same vulnerable sink.
- Code path:
runc run cve-ptmx-test -b /bundle→prepareRootfs→doSetupDev(rootFd, config)→setupPtmx/setupDevSymlinks.
runc create+runc startentry point.- A different CLI invocation that ultimately runs the same
prepareRootfscode in the container init. - Code path:
runc create cve-ptmx-test -b /bundle && runc start cve-ptmx-test→prepareRootfs→doSetupDev→setupPtmx/setupDevSymlinks.
- A different CLI invocation that ultimately runs the same
/dev/ptssymlink (/bundle/rootfs/dev/pts -> /bundle/controlled_dev).- A different symlink location inside
/dev. The vulnerablesetupPtmxonly operates on/dev/ptmx, so the/dev/ptssymlink does not redirect the host-side file operations. This candidate was ruled out.
- A different symlink location inside
Results:
Candidates 1 and 2 reproduced on runc 1.3.5 (decoy
ptmxfile removed and replaced byptmx -> pts/ptmxplus the hardcoded/devsymlinks).Candidates 1 and 2 did not reproduce on runc 1.3.6; the decoy was preserved.
Candidate 3 did not reproduce on either version.
Package / component: opencontainers/runc
Affected versions: prior to 1.3.6, 1.4.3, and 1.5.0 (as per advisory)
Risk level: low (per upstream advisory)
Consequences: On the vulnerable version, an attacker-supplied container image with a
/devsymlink can cause runc to delete a file namedptmxand create a fixed set of symlinks in an attacker-chosen host directory. The variant triggers demonstrate the same impact through a relative symlink and through thecreate/startentry point.
Impact Parity
- Disclosed / claimed maximum impact: Arbitrary deletion of a host
ptmxfile and creation of a hardcoded set of symlinks in a host directory reachable via a malicious/devsymlink. - Reproduced impact from this variant run: On runc 1.3.5, both the relative
/devsymlink and thecreate/startpath removed the decoy file and created the expected symlinks in the attacker-controlled directory. On runc 1.3.6, the decoy was preserved in both cases. - Parity:
fullfor the documented filesystem-integrity impact. The variants did not demonstrate privilege escalation or code execution, which is consistent with the advisory's low-severity rating. - Not demonstrated: A bypass of the fixed version.
Root Cause
The root cause is the same as the original CVE: before the container switches into its rootfs, setupPtmx and setupDevSymlinks used path strings built with filepath.Join(config.Rootfs, "/dev/...") and then called os.Remove / os.Symlink. If the image's /dev is a symlink (absolute or relative) to a directory outside the rootfs, those path operations follow the symlink and affect the host filesystem. The runc create/runc start path reaches the same prepareRootfs code, so the same bug is exercised.
Upstream fix commits:
a8e53f2c(release-1.3) /864db8042dbb(main) —rootfs: make /dev initialisation code fd-based9432ad3a(release-1.3) /66acd48f9d42(main) —rootfs: make cgroupv1 subsystem symlinks fd-based(sibling hardening)d934454b—merge CVE-2026-41579 fixes into release-1.3
Reproduction Steps
The reproduction is implemented in bundle/vuln_variant/reproduction_steps.sh. At a high level it:
- Verifies Docker is available.
- Downloads the vulnerable runc release binary (
v1.3.5) and the fixed release binary (v1.3.6) if not cached. - Builds a minimal OCI rootfs from the official
busyboximage if not cached. - Builds two privileged Docker images (
repro-runc-vulnandrepro-runc-fixed) that each contain one runc binary and the rootfs. - For each variant, starts a fresh privileged container and:
- Generates an OCI bundle with
runc spec, disables the terminal, and sets the command to/bin/true. - Sets up the variant symlink layout (relative
/devsymlink,/dev/ptssymlink, or absolute/devsymlink for thecreate/startentry point). - Creates a decoy file named
ptmxin the target directory. - Runs
runc runorrunc create/runc start. - Inspects whether the decoy was preserved, replaced by a symlink, or deleted.
- Generates an OCI bundle with
- Compares the vulnerable and fixed results for each variant and exits 1 because no variant bypasses the fixed version.
Expected evidence:
- Vulnerable (1.3.5): for the relative
/devsymlink and thecreate/startentry point, the decoyptmxis replaced by a symlink and the target directory contains the hardcoded/devsymlinks (core,fd,stdin,stdout,stderr). - Fixed (1.3.6): the decoy
ptmxremains a regular file and no new symlinks appear in the target directory. /dev/ptssymlink: no version deletes the decoy; the symlink is preserved and/dev/ptmxis created inside the real/devdirectory.
Evidence
bundle/logs/vuln_variant/relative_dev_symlink_vuln.log— vulnerable runc 1.3.5 replaces the decoy withptmx -> pts/ptmxand creates the other hardcoded symlinks in the target directory.bundle/logs/vuln_variant/relative_dev_symlink_fixed.log— fixed runc 1.3.6 preserves the decoy.bundle/logs/vuln_variant/create_start_entrypoint_vuln.log— vulnerable runc 1.3.5 reached throughrunc create/runc startreplaces the decoy with symlinks.bundle/logs/vuln_variant/create_start_entrypoint_fixed.log— fixed runc 1.3.6 reached throughrunc create/runc startpreserves the decoy.bundle/logs/vuln_variant/pts_symlink_vuln.log— decoy preserved;/dev/ptssymlink is left in place and/dev/ptmxis created in the real/devdirectory.bundle/logs/vuln_variant/pts_symlink_fixed.log— same safe behavior on the fixed version.bundle/vuln_variant/runtime_manifest.json— runtime evidence manifest produced by the script.bundle/logs/vuln_variant/fixed_version.txt— exact tested fixed revision.bundle/logs/vuln_variant/vulnerable_version.txt— exact tested vulnerable revision.
Key excerpts:
Relative /dev symlink on vulnerable 1.3.5:
VARIANT: relative_dev_symlink
RUN_VERSION: runc version 1.3.5
BEFORE: /bundle/controlled_dev contents
-rw-r--r-- 1 root root 10 ... ptmx
...
AFTER: /bundle/controlled_dev contents
lrwxrwxrwx 1 root root 11 ... core -> /proc/kcore
lrwxrwxrwx 1 root root 13 ... fd -> /proc/self/fd
lrwxrwxrwx 1 root root 8 ... ptmx -> pts/ptmx
...
RESULT: decoy replaced by symlink
Relative /dev symlink on fixed 1.3.6:
VARIANT: relative_dev_symlink
RUN_VERSION: runc version 1.3.6
BEFORE: /bundle/controlled_dev contents
-rw-r--r-- 1 root root 10 ... ptmx
...
AFTER: /bundle/controlled_dev contents
-rw-r--r-- 1 root root 10 ... ptmx
RESULT: decoy preserved
create/start on vulnerable 1.3.5:
VARIANT: create_start_entrypoint
RUN_VERSION: runc version 1.3.5
RUNC_EXIT: create_rc=0 start_rc=0
AFTER: /bundle/controlled_dev contents
lrwxrwxrwx 1 root root 8 ... ptmx -> pts/ptmx
...
RESULT: decoy replaced by symlink
create/start on fixed 1.3.6:
VARIANT: create_start_entrypoint
RUN_VERSION: runc version 1.3.6
RUNC_EXIT: create_rc=0 start_rc=0
AFTER: /bundle/controlled_dev contents
-rw-r--r-- 1 root root 10 ... ptmx
RESULT: decoy preserved
Recommendations / Next Steps
- Upgrade runc to a patched version: 1.3.6, 1.4.3, or 1.5.0 (or later). The 1.3.6 binary was tested here and blocks all variant candidates.
- Higher-level runtimes should not rely on a
/devsymlink in the container image and should verify they are using a patched runc version. - Regression tests should include both absolute and relative
/devsymlinks, and should exercise bothrunc runandrunc create/runc start. - The sibling cgroupv1 symlink fix (
9432ad3a) should be kept in any backport; it is part of the same defense-in-depth change. - If a future change reintroduces mounting on top of the container root,
rootFdmust be reopened after that mount, as noted in theprepareRootfscomment.
Additional Notes
- The script is idempotent: it reuses cached binaries and Docker images, runs each variant in a fresh container, and writes unique log files. It was executed twice and produced the same verdict.
- File bind-mounts did not work in this Docker environment, so the script generates inner shell scripts on the host and pipes them into the container via
sh -s < script. This is documented as the working transport mechanism. - The reproduction uses the real
runcCLI binary and the real OCI bundle execution path, not a mocked environment. - The privileged Docker-in-Docker container is required because the sandbox host lacks
CAP_SYS_ADMINand a writable cgroup hierarchy; inside the privileged container, runc has the capabilities needed to create a real container. - No sanitizer or crash is involved; the proof relies on filesystem-state differences between vulnerable and fixed versions.
- Source identity for the tested binaries is recorded in
bundle/vuln_variant/source_identity.jsonandbundle/logs/vuln_variant/fixed_version.txt/vulnerable_version.txt.
CVE-2026-41579 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-41579
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-41579
FAQ: CVE-2026-41579
How does the CVE-2026-41579 symlink attack work?
/dev symlinked to an attacker-chosen host directory causes runc to delete an existing file named ptmx on the host and create a small, fixed set of device symlinks in that attacker-controlled directory. This is not exploitable under Docker, but affects other container tooling built on runc that does not mask /dev with a top-level read-only layer.Which runc versions are affected by CVE-2026-41579, and where is it fixed?
How severe is CVE-2026-41579?
How can I reproduce CVE-2026-41579?
References for CVE-2026-41579
Authoritative sources for CVE-2026-41579 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.