Python tarfile `filter="data"` Bypass via PATH_MAX/realpath Confusion (CVE-2025-4517)
by Esteban Zárate — [github.com/estebanzarate](https://github.com/estebanzarate) (cleanup/simplification; original discovery credited to the Python Security Response Team) · 2026-07-06
- Severity
- Critical
- CVE
- CVE-2025-4517
- Category
- misc
- Affected product
- Python standard library tarfile module — filter="data" / filter="tar" extraction filters (PEP 706)
- Affected versions
- Python 3.8.0 through 3.13.1 (per source repository)
- Disclosed
- 2026-07-06
- Patch status
- patched
References
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-06 |
| Last Updated | 2026-07-06 |
| Author / Researcher | Esteban Zárate — github.com/estebanzarate (cleanup/simplification; original discovery credited to the Python Security Response Team) |
| CVE / Advisory | CVE-2025-4517 |
| Category | misc |
| Severity | Critical |
| CVSS Score | 9.4 (per NVD) |
| Status | Weaponized |
| Tags | python, tarfile, path-traversal, sandbox-bypass, path_max, realpath, symlink, cwe-22, stdlib |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Python standard library tarfile module — filter="data" / filter="tar" extraction filters (PEP 706) |
| Versions Affected | Python 3.8.0 through 3.13.1 (per source repository) |
| Language / Platform | Python 3 PoC, exploiting a Linux/POSIX filesystem PATH_MAX (4096-byte) limitation in path resolution |
| Authentication Required | Depends on the integrating application — any code path that calls tarfile.extractall(filter="data") (or "tar") on an attacker-influenced archive is affected. This PoC’s staged demo additionally requires local access with sudo rights to trigger a specific backup-restore script. |
| Network Access Required | No for this PoC’s local trigger — but any networked service that extracts attacker-supplied tar archives (e.g. an upload/import/backup-restore feature) using the affected filter would be remotely exploitable via the same technique. |
Summary
Python’s tarfile module added extraction filters (filter="data"/"tar", PEP 706, enabled by default since Python 3.12 and backported) specifically to prevent unsafe archive extraction — path traversal, symlink escapes, and writes outside the destination directory. CVE-2025-4517 is a bypass of that sandbox: the filter’s safety check calls os.path.realpath() to resolve a member’s path and confirm it stays inside the extraction directory, but realpath() silently stops resolving symlinks once the accumulated path exceeds PATH_MAX (4096 bytes on Linux) and returns the unresolved (safe-looking) path instead of raising an error. The kernel, however, has no such limit during the actual extraction/write syscalls and fully resolves the same symlink chain, escaping the intended sandbox. This PoC builds a tar archive with deeply nested 247-character directory names and chained symlinks that push the resolved path just past PATH_MAX, then uses the resulting confusion to write an SSH public key to /root/.ssh/authorized_keys, granting the attacker root SSH access.
Vulnerability Details
Root Cause
filter="data"’s safety check resolves each tar member’s destination path with os.path.realpath() before extracting, to make sure it does not escape the destination directory (directly, or via a malicious symlink target). On Linux, realpath() (like the underlying kernel path-resolution routines it wraps in Python’s C implementation) is bounded by PATH_MAX (4096 bytes). When a symlink chain’s cumulative resolved path grows past that limit, resolution stops early and the function returns a truncated/unresolved path rather than erroring — so the security check evaluates a path it believes is still safely inside the staging directory. During actual extraction, however, the OS resolves the full symlink chain without the same short-circuit, and the write lands wherever the fully-resolved chain points — outside the sandbox.
The PoC constructs this condition directly in create_exploit_tar():
| |
Sixteen levels of 247-character directory names (each paired with a single-character symlink pointing at the long name) push the cumulative resolved path length well past 4096 bytes. A final escape symlink then appends ../../../../../../../root to that already-oversized path. When filter="data" calls realpath() on escape, resolution stops at PATH_MAX and the check sees a path that still appears to be inside the staging/extraction directory — so it passes. The kernel, resolving the same chain during the actual write, has no such limit and correctly walks all the way to /root. A regular file member (escape/.ssh/authorized_keys) written “through” that symlink is therefore physically written to /root/.ssh/authorized_keys.
Attack Vector
- Attacker builds a malicious tar archive containing 16 levels of 247-character nested directories and paired symlinks, a final long symlink performing
../traversal, and anescapesymlink whose target is the oversized path plus../../../../../../../root. - Attacker delivers/stages this tar wherever a privileged process will extract it with
filter="data"(in this PoC, copied into a backup directory consumed by asudo-invoked restore script; more broadly, any upload/import/CI artifact/backup-restore pathway that extracts attacker-supplied tars). - The victim process calls
tarfile.extractall(filter="data")(or"tar") on the archive.os.path.realpath()truncates resolution atPATH_MAXand the safety check passes. - The kernel fully resolves the symlink chain during the actual extraction write, and a file nominally destined for
escape/.ssh/authorized_keysis physically written to/root/.ssh/authorized_keys. - The attacker’s SSH public key is now trusted for the
rootaccount; the attacker authenticates via SSH using the matching private key, confirmed byidreturninguid=0(root).
Impact
Any application, service, or automated pipeline that extracts attacker-influenced tar archives while relying on filter="data"/"tar" for sandboxing (backup/restore tools, CI/CD artifact unpacking, package installers, file-upload/import features, container image layer handling, etc.) can have arbitrary files written outside the intended extraction directory. In the demonstrated scenario this escalates to root SSH access via authorized_keys injection; in general it enables path traversal, arbitrary file write/overwrite, and — depending on what paths are writable — code execution or privilege escalation.
Environment / Lab Setup
Vulnerable component: Python 3.8.0 - 3.13.1 `tarfile` module (filter="data"/"tar").
PoC-staged scenario (as scripted): a privileged backup/restore workflow —
- Backup files land in /opt/backup_clients/backups/
- A restore script /opt/backup_clients/restore_backup_clients.py is invoked via `sudo` to extract them
- Requires local shell access with sudo rights to trigger that specific script (this is scenario/lab scaffolding
the PoC author used to demonstrate impact; the underlying tarfile bug itself does not require sudo — it
requires only that *some* process extract an attacker-influenced tar with the affected filter).
Attacker tooling: Python 3, `ssh-keygen`, `ssh` client.
Proof of Concept
PoC Script
See
CVE-2025-4517.pyin this folder.
| |
Example run (from the source repository’s documented output):
[+] SSH keypair generated: /tmp/cve_2025_4517_key
[*] Creating exploit tar...
[+] Exploit tar created: /tmp/cve_2025_4517_exploit.tar
[*] Deploying exploit to: /opt/backup_clients/backups/backup_9999.tar
[+] Exploit deployed successfully
[*] Triggering extraction via vulnerable script...
[+] Extraction completed
[*] Verifying exploit via SSH...
[+] SUCCESS! SSH as root confirmed: uid=0(root) gid=0(root) groups=0(root)
============================================================
[+] EXPLOITATION SUCCESSFUL!
[+] SSH into root with: ssh -i /tmp/cve_2025_4517_key root@localhost
============================================================
Detection & Indicators of Compromise
Tar archive members with:
- directory/symlink names of unusual fixed length (e.g. 247 or 254 characters)
- deeply nested (10+ level) directory chains composed of such long names
- symlink targets containing long runs of "../" traversal sequences
- a final symlink ("escape"-style) whose resolved target combines an oversized path with additional "../" segments
Signs of compromise:
- Unexpected entries appearing in
/root/.ssh/authorized_keys(or other sensitive files) with no corresponding legitimate administrative change. - Backup/restore or extraction processes writing files outside their designated staging/output directory.
- Tar archives on disk containing abnormally long directory/symlink names or deeply nested symlink chains.
- SSH logins to privileged accounts from unfamiliar keys shortly after a backup-restore or archive-import operation.
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to a Python version with the corrected tarfile filter logic (patched releases address the PATH_MAX/realpath() truncation issue in filter="data"/"tar") — see the official Python security advisory for exact fixed versions. |
| Interim mitigation | Avoid extracting untrusted/attacker-influenced tar archives with any privileged process; reject archives containing abnormally long paths or deep symlink nesting prior to extraction; extract untrusted archives in an isolated, unprivileged, disposable environment (e.g. container with no sensitive mounted paths) regardless of the filter argument used. |
References
Notes
Mirrored from https://github.com/estebanzarate/CVE-2025-4517-Python-tarfile-filter-data-Bypass-PoC on 2026-07-06. Complete, working exploit: generates a malicious tar exploiting PATH_MAX/realpath confusion to bypass filter="data" and write SSH keys, escalating to root. The staged backup/restore/sudo scenario in the script is lab scaffolding to demonstrate impact end-to-end; the core tarfile bypass itself applies to any process extracting attacker-influenced archives with the affected filter.
| |