CVE-2026-82049 Overview
CVE-2026-82049 is a filter bypass vulnerability in the CPython tarfile module affecting Python 3.13 and earlier. The data and tar extraction filters fail to safely handle crafted archives that contain a hard link pointing to a symbolic link. During extraction, os.link() duplicates the symlink rather than the target file, allowing the resulting link to reference a path outside the destination directory. Attackers can leverage this behavior to modify file permissions, alter modification times, or expose the contents of files that reside outside the intended extraction tree. The flaw is classified under CWE-59: Improper Link Resolution Before File Access (Link Following).
Critical Impact
A malicious tar archive processed by a trusted Python application can disclose or tamper with files outside the extraction directory, bypassing the safety guarantees of the data and tar filters.
Affected Products
- CPython 3.13 and earlier
- Python tarfile module using data or tar extraction filters
- Downstream applications and package managers relying on tarfile.extractall() for untrusted archives
Discovery Timeline
- 2026-09-14 - CVE CVE-2026-82049 published to NVD
- 2026-09-14 - Public discussion on Openwall OSS Security and Python Security Announcement
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-82049
Vulnerability Analysis
The tarfile module's data and tar filters are intended to prevent extracted content from escaping the destination directory. When processing a hard link entry, the extractor calls os.link(tarinfo._link_target, targetpath) without resolving the link target. If tarinfo._link_target already refers to a symbolic link previously written into the extraction tree, the new hard link duplicates that symlink at a shallower location. The relative path stored in the symlink then resolves outside the destination directory. Follow-up write, chmod, or utime operations against the hard-linked path act on files anywhere the process has permission to reach.
Root Cause
The filters validated that individual entries did not directly escape the destination, but they did not account for the composition of a hard link with a previously extracted symbolic link. Because os.link() on Linux historically did not follow symlinks, the duplicated inode retained the symlink semantics rather than binding to the actual target file.
Attack Vector
An attacker crafts a tar archive containing (1) a symbolic link whose target uses relative traversal such as ../../etc/passwd, and (2) a hard link entry that references the symlink path. When a victim extracts the archive with the data or tar filter, the hard link is materialized at a location controlled by the attacker but resolves to a file outside the extraction root. Subsequent metadata application from the archive changes permissions or timestamps on the external file, and reading the hard-linked path returns the external file's contents.
if os.path.lexists(targetpath):
# Avoid FileExistsError on following os.link.
os.unlink(targetpath)
- os.link(tarinfo._link_target, targetpath)
+ # Resolve the target so the hard link points to the file
+ # itself. Otherwise os.link() may duplicate a symlink to a
+ # shallower location, where its relative target escapes the
+ # destination directory. (CVE-2026-82049)
+ os.link(os.path.realpath(tarinfo._link_target), targetpath)
return
except symlink_exception:
keyerror_to_extracterror = True
Source: python/cpython commit b38be2e6
Detection Methods for CVE-2026-82049
Indicators of Compromise
- Tar archives containing both symbolic link entries and hard link entries where the hard link's linkname matches a previously listed symlink path.
- Unexpected changes to mtime or permission bits on files outside application extraction directories following archive processing.
- Files inside an extraction tree whose stat() inode matches a file located outside the extraction root.
Detection Strategies
- Statically inspect untrusted archives using tarfile.TarFile.getmembers() and flag any TarInfo with type == LNKTYPE whose linkname was previously seen as SYMTYPE.
- Enable Python's audit hooks (sys.addaudithook) to log os.link calls originating from tarfile and correlate source paths against expected extraction roots.
- Monitor Python process file system telemetry for link() or linkat() syscalls that resolve outside the extraction working directory.
Monitoring Recommendations
- Alert on modifications to sensitive files such as /etc/passwd, SSH authorized_keys, or CI/CD credentials that occur during or immediately after archive extraction workflows.
- Track inventory of Python runtime versions across build agents, container images, and developer workstations to identify hosts still on CPython 3.13 or earlier.
- Log and review use of tarfile.extractall(filter='data') and filter='tar' in application code and CI pipelines that ingest external archives.
How to Mitigate CVE-2026-82049
Immediate Actions Required
- Upgrade CPython to a patched release that includes the fix from GH-157191 and backport commit b8f23e30.
- Audit applications, installers, and CI pipelines that call tarfile.extractall() or tarfile.extract() on archives from external sources.
- Rebuild and redeploy container images and virtual environments that ship a vulnerable Python interpreter.
Patch Information
The upstream fix modifies Lib/tarfile.py to call os.link(os.path.realpath(tarinfo._link_target), targetpath), forcing hard links to bind to the resolved file rather than a symbolic link. A companion change in commit 5a57248b updates os.link() on Linux to always call linkat() when available so that follow_symlinks behavior is consistent. See the Python Security Announcement for release-specific version numbers.
Workarounds
- Extract untrusted archives inside a disposable sandbox such as a container, chroot, or unprivileged user namespace with no access to sensitive files.
- Pre-scan archives and reject any entry combining LNKTYPE with a linkname that resolves to a SYMTYPE entry in the same archive.
- Replace tarfile.extractall() with a custom extractor that resolves and validates each TarInfo._link_target against the destination root before calling os.link().
# Verify installed Python version and upgrade to a patched release
python3 --version
# Example: pin patched Python in a container build
# Dockerfile
FROM python:3.13-slim
RUN python3 -c "import sys; assert sys.version_info >= (3, 13, 8), 'Upgrade required for CVE-2026-82049'"
# Safer extraction pattern for untrusted archives
python3 - <<'PY'
import tarfile, os
dest = os.path.abspath("./extract")
os.makedirs(dest, exist_ok=True)
with tarfile.open("input.tar") as tf:
tf.extractall(dest, filter="data")
PY
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.