A fifteen-year-old bug in the Python standard library is still one of the most common ways attackers turn "download and unpack an archive" into arbitrary file write and, frequently, remote code execution. CVE-2007-4559 is a path traversal vulnerability in the tarfile module's extract() and extractall() functions: when a TAR archive contains member names with ../ sequences (or absolute paths, or symlinks that point outside the destination), Python happily writes those files wherever the archive tells it to — overwriting SSH keys, cron jobs, application code, CI configuration, or anything else the extracting process has permission to touch. Despite being assigned a CVE in 2007, the flaw was largely ignored for over a decade until Trellix researchers demonstrated in 2022 that it was still present, unpatched, and reachable in an estimated 350,000+ open-source repositories that call tarfile without validating archive contents. It has since become a textbook example of how "fixed in the codebase" and "fixed in practice" are two very different things — and why software supply chain security teams need visibility into where vulnerable extraction patterns actually run, not just whether a patch exists upstream.
What the vulnerability actually does
tarfile.extractall() and tarfile.extract() write each member of a TAR archive to disk using the path stored inside the archive's own metadata, without checking whether that path escapes the intended extraction directory. A malicious archive can include an entry named ../../../../home/user/.ssh/authorized_keys, or a symlink member pointing at /etc/cron.d/, and Python will follow it. Because this happens purely through path string manipulation — no memory corruption, no exotic parsing bug — the exploit is trivial to construct and works identically across every Python version that predates the fix.
The practical impact depends entirely on what calls extractall() and with what privileges. In the wild, this pattern shows up in:
- ML/AI pipelines that unpack downloaded model weights, datasets, or checkpoints (
.tar.gzarchives are extremely common in the ML ecosystem) - Package managers, plugin systems, and installers that unpack third-party archives
- Backup and restore utilities
- CI/CD steps that extract build artifacts, container layers, or cached dependencies
- Web applications that accept user-uploaded archives
In each case, a crafted archive from an untrusted source — a malicious PyPI package, a poisoned model repository, a user upload — can result in arbitrary file overwrite, and in many configurations that escalates to remote code execution (overwriting a .bashrc, a systemd unit file, a webshell dropped into a web root, or a script that will later be executed with elevated privileges).
Affected versions and components
Every CPython release prior to the introduction of PEP 706's extraction filters was vulnerable by default, because tarfile.extract() and extractall() performed no path sanitization at all. That covers essentially the entire Python 2.x line (now end-of-life) and Python 3.x releases through 3.11.
The fix is opt-in even where it exists: Python 3.12 introduced the filter argument along with tarfile.data_filter and tarfile.tar_filter, but the historically permissive behavior (filter=None, i.e., fully trusting the archive) remained the default, merely emitting a DeprecationWarning. It is not enough to be running a "patched" Python — code has to actually pass filter='data' (or set TarFile.extraction_filter globally) to get the protection. This is the detail that trips up most remediation efforts: upgrading the interpreter alone does not close the hole unless the calling code, or one of its dependencies, was also updated to request safe extraction.
Because tarfile is a standard library module, "affected components" effectively means any first-party or third-party Python code that calls extract()/extractall() on archives originating outside of full trust boundaries — which, per Trellix's 2022 research, includes machine learning frameworks, DevOps tooling, backup utilities, and countless smaller libraries pulled in transitively by application dependency trees.
CVSS, EPSS, and KEV context
NVD's original scoring for CVE-2007-4559 is a CVSSv2 base score of 5.0 (Medium) — reflecting the era's scoring model, which weighted network-exploitable, no-authentication, integrity-impacting bugs more conservatively than modern CVSSv3/v4 would. Most contemporary risk platforms recalculate this closer to High severity once the "user-assisted" precondition (someone or something must extract the malicious archive) is factored against the real-world blast radius of arbitrary file write, which frequently chains to full code execution.
CVE-2007-4559 is not currently listed in CISA's Known Exploited Vulnerabilities (KEV) catalog, since there is no confirmed record of a specific, attributed in-the-wild campaign tied to the CVE identifier itself. That said, its EPSS (Exploit Prediction Scoring System) profile has consistently sat well above the median for CVEs of its age — a reflection of how trivially it can be weaponized, how many automated scanners actively probe for it, and how large its reachable footprint is across the open-source ecosystem. Age and a "Medium" legacy score are poor proxies for actual exploitability here; this is a case where reachability and exposure analysis tell you far more than the score alone.
Timeline
- 2007 — CVE-2007-4559 is assigned, related to an earlier, similar directory-traversal report (CVE-2001-1267). NVD publishes the advisory with a CVSSv2 score of 5.0.
- 2007–2021 — The flaw remains technically documented but receives little attention;
tarfile.extractall()continues to be widely used without archive validation across the Python ecosystem. - August 2022 — Trellix researchers Kasimir Schulz, Tom Bowyer, and colleagues publish "The 15-Year-Old Bug That Won't Die," showing the vulnerability is still present and reachable in an estimated 350,000+ GitHub repositories, spanning ML frameworks, CI tooling, and application code. The research renews CVE-2007-4559's relevance industry-wide and triggers a wave of downstream advisories for projects that called
tarfileinsecurely. - June 2023 — CPython maintainers backport the
filtermechanism to supported maintenance branches (3.8, 3.9, 3.10, 3.11) ahead of the formal PEP. - October 2023 — Python 3.12 ships PEP 706, introducing
tarfile.data_filter,tarfile.tar_filter, and theTarFile.extraction_filterclass attribute, withfilter=Nonestill the default (triggering aDeprecationWarning). - 2025 (Python 3.14) — The default extraction filter changes from "fully trusting" to
'data', making safe extraction the out-of-the-box behavior for new Python installs — nearly two decades after the original disclosure.
Remediation steps
- Upgrade your Python interpreter to a version that includes the
filterargument: 3.8.17+, 3.9.17+, 3.10.12+, 3.11.4+, or 3.12 and later. This is necessary but not sufficient. - Explicitly pass a safe filter on every extraction call:
tar.extractall(path, filter='data')(or'tar'if you need to preserve more archive semantics but still block traversal and unsafe links). Do not rely on the default until you're fully on Python 3.14+. - Set the filter globally where you can't touch every call site:
tarfile.TarFile.extraction_filter = staticmethod(tarfile.data_filter)enforces safe extraction process-wide, including inside third-party dependencies that haven't updated their own calls. - Audit for indirect exposure. The vulnerable pattern often lives in a dependency, not your own code — ML libraries unpacking model archives, packaging tools, backup utilities. Grep your dependency tree for
extractall(andextract(calls and verify each one specifies a safe filter. - Treat every archive from outside your trust boundary as hostile — uploaded files, downloaded model weights, third-party packages, CI artifacts pulled from external registries. Validate member paths yourself (
os.path.realpathplus acommonpathcontainment check) if you're stuck on an environment where you can't apply the filter. - Extract in isolation. Run extraction in a sandboxed, low-privilege, ephemeral context (dedicated temp directory, non-root user, restricted filesystem access) as defense-in-depth, so a missed edge case doesn't translate directly into a host compromise.
- Re-test after upgrading. Confirm with a proof-of-concept archive containing a
../traversal member that extraction now fails safely (raisesOutsideDestinationErroror equivalent) instead of writing outside the target directory.
How Safeguard Helps
Legacy, "already patched" CVEs like this one are exactly where point-in-time scanning falls short — the flaw looks closed at the interpreter level while still being live in application logic that never adopted the safe filter. Safeguard's reachability analysis traces every call site of tarfile.extract() and extractall() across your first-party code and dependency graph, distinguishing archives extracted from genuinely untrusted sources from those handling fully trusted, internal data, so your team can prioritize the calls that matter instead of chasing every occurrence in the codebase. Griffin AI, Safeguard's autonomous security analyst, correlates that reachability signal with your runtime and deployment context to flag which services are actually exposed to attacker-controlled archives — models, uploads, CI artifacts — versus which are theoretical. Our SBOM generation and ingest pipeline gives you continuous, version-accurate visibility into every component that transitively pulls in vulnerable extraction logic, even as your dependency tree shifts release to release. And where remediation is straightforward — adding filter='data' to an extraction call or bumping a pinned interpreter version — Safeguard can open an auto-fix pull request directly against the affected repository, turning a fifteen-year-old advisory into a same-day merge instead of another line item on a backlog.