Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-11940

CVE-2026-11940: Python tarfile Path Traversal Vulnerability

CVE-2026-11940 is a path traversal flaw in Python's tarfile module that allows attackers to bypass extraction filters using crafted archives with hardlinks and symlinks to write files outside the destination directory.

Published:

CVE-2026-11940 Overview

CVE-2026-11940 is a path traversal vulnerability [CWE-22] in the Python tarfile module that allows malicious archives to escape the destination directory during extraction. The flaw affects tarfile.extractall() when used with the data or tar extraction filters. A crafted archive can place a hardlink that references a symlink stored at a deeper path than the hardlink itself, causing the extraction fallback to recreate the symlink at the hardlink's shallower path. The resulting symlink can point outside the destination, enabling out-of-destination file reads or writes. This issue is an incomplete fix of CVE-2025-4330.

Critical Impact

Attackers controlling tar archive contents can write or read files outside the intended extraction directory, leading to arbitrary file overwrite and potential code execution in downstream workflows.

Affected Products

  • CPython tarfile module across maintained branches (fixes landed in 3.13, 3.14, and 3.15 development branches)
  • Applications using tarfile.extractall() with the data or tar filter
  • Build, CI, and packaging tools that extract untrusted tar archives via the standard library

Discovery Timeline

  • 2026-06-23 - CVE-2026-11940 published to NVD
  • 2026-06-23 - Last updated in NVD database

Technical Details for CVE-2026-11940

Vulnerability Analysis

The Python tarfile extraction filters introduced in PEP 706 enforce safety checks for member names, symlink targets, and link references. When a tar member is a hardlink pointing to a symlink that exists at a deeper directory level within the archive, the fallback path in makelink_with_filter validated the link target against the symlink's original archived location. However, the actual filesystem object was recreated at the hardlink's shallower path. Because directory depth changes the resolution of relative symlink targets, a target that was considered contained at the deeper path can escape the destination once materialized at the shallower path. The result is a writable symlink pointing outside the extraction root.

Root Cause

The root cause is a mismatch between the filter's validation context and the file's creation context. The filter judged the symlink safe relative to the archived path of the original symlink. The fallback then created the symlink at the hardlink's name, which resolves relative paths differently. This logic gap was not addressed by the original fix for CVE-2025-4330.

Attack Vector

An attacker supplies a malicious .tar archive to any application that calls tarfile.extractall() with the data or tar filter. Upon extraction, a symlink is materialized pointing outside the destination directory. Subsequent extracted members or later application logic that follows the symlink can read or overwrite arbitrary files accessible to the extracting process.

python
# Patch excerpt from Lib/tarfile.py (CPython)
                    "makelink_with_filter: if filter_function is not None, "
                    + "extraction_root must also not be None")
            try:
                filter_function(
                    unfiltered.replace(name=tarinfo.name, deep=False),
                    extraction_root)
                filtered = filter_function(unfiltered, extraction_root)
            except _FILTER_ERRORS as cause:
                raise LinkFallbackError(tarinfo, unfiltered.name) from cause

Source: CPython commit 27dd970b. The fix runs the filter a second time using the hardlink's actual target name with deep=False, ensuring validation occurs in the same context where the symlink is created.

Detection Methods for CVE-2026-11940

Indicators of Compromise

  • Symlinks present after extraction whose canonical target resolves outside the destination directory.
  • Unexpected file modifications in directories adjacent to or above the extraction root following a tar archive extraction.
  • Application logs showing LinkFallbackError exceptions from tarfile, which indicate filter fallback paths were exercised.

Detection Strategies

  • Audit Python applications and CI pipelines for calls to tarfile.extractall() and tarfile.TarFile.extract(), verifying that a filter is set and the interpreter is patched.
  • Inspect tar archives prior to extraction for entries combining hardlinks and symlinks at differing path depths.
  • Compare pre- and post-extraction filesystem state to flag any files created outside the intended destination tree.

Monitoring Recommendations

  • Monitor process telemetry for python processes invoking symlink and link syscalls during archive extraction in build and packaging workflows.
  • Track file-write events outside designated extraction directories from interpreters processing untrusted input.
  • Alert on Python tracebacks referencing tarfile.py and LinkFallbackError in application logs.

How to Mitigate CVE-2026-11940

Immediate Actions Required

  • Upgrade to a patched CPython release once available for your branch (3.13, 3.14, 3.15) per the Python Security Announcement.
  • Inventory all internal tools, container images, and CI runners that extract untrusted tar archives using tarfile.
  • Refuse extraction of archives from untrusted sources until patched interpreters are deployed.

Patch Information

The upstream fix is tracked in GitHub issue 151558 and pull request 151559. Backports are merged in commit 27dd970b, commit 672825e2, commit 771d12dd, and commit 79c06bd5.

Workarounds

  • Extract untrusted archives inside a disposable sandbox such as an unprivileged container or chroot with no sensitive paths reachable via symlink resolution.
  • Pre-scan archives and reject any member whose type is LNKTYPE (hardlink) referencing a SYMTYPE (symlink) target stored at a different directory depth.
  • After extraction, walk the destination tree and remove or quarantine any symlink whose resolved target falls outside the extraction root.
bash
# Reject archives mixing hardlinks and symlinks at differing depths
python3 - <<'EOF'
import sys, tarfile
path = sys.argv[1]
with tarfile.open(path) as tf:
    syms = {m.name: m for m in tf.getmembers() if m.issym()}
    for m in tf.getmembers():
        if m.islnk() and m.linkname in syms:
            if m.name.count('/') != syms[m.linkname].name.count('/'):
                print(f"UNSAFE: hardlink {m.name} -> symlink {m.linkname}")
                sys.exit(1)
print("OK")
EOF

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.