## https://sploitus.com/exploit?id=7AB33D68-B5EA-5FDB-ADD0-5EDB6DCB9186
# CVE-2025-4517 / CVE-2025-4330 β Python `tarfile` Data Filter Bypass via PATH_MAX Overflow
**Author:** [0xDTC](https://github.com/0xDTC)
**CVEs:** [CVE-2025-4517](https://vulners.com/cve/CVE-2025-4517) / [CVE-2025-4330](https://www.wiz.io/vulnerability-database/cve/cve-2025-4330)
**Advisory:** [GHSA-6r6c-684h-9j7p](https://github.com/advisories/GHSA-6r6c-684h-9j7p)
**CPython Fix:** [PR #135037](https://github.com/python/cpython/pull/135037)
## Overview
Python's `tarfile.extractall(filter="data")` is supposed to safely extract tar archives by preventing path traversal (absolute paths, `..` sequences, and symlinks escaping the destination). However, a bug in `os.path.realpath(strict=False)` allows this filter to be bypassed entirely.
When the resolved path exceeds **PATH_MAX** (4096 bytes on Linux), `os.path.realpath()` silently stops resolving symlinks and falls back to **string manipulation**. This means a carefully crafted symlink chain can trick Python into thinking a symlink resolves inside the extraction directory when it actually escapes to `/`.
### Affected Versions
| Branch | Affected | Fixed In |
|--------|----------|----------|
| 3.12.x | 3.12.0 β 3.12.10 | 3.12.11 |
| 3.13.x | 3.13.0 β 3.13.3 | 3.13.4 |
| 3.14.x | 3.14.0a1 β 3.14.0a7 | 3.14.0b1 |
## How It Works
The exploit constructs a tar archive containing:
1. **16 directory/symlink pairs** β each directory has a ~240-character name, with a short (1-char) symlink pointing to it
2. **A 254-character escaping symlink** β placed at the end of the short chain, pointing back 16 levels
3. **An "escape" symlink** β traverses the chain and exceeds PATH_MAX, tricking `realpath`
4. **A regular file** β written through the escape symlink to an arbitrary location outside the extraction directory
The core insight: following `a/b/c/.../p` through short symlinks stays under PATH_MAX, but `realpath` expands each to the ~240-char real directory name. By the time it hits the 254-char link name, the resolved path exceeds 4096 bytes and `realpath` gives up β silently returning an incorrect result.
## Data Flow Diagram
```mermaid
flowchart TD
A["Attacker crafts malicious tar"] --> B["tar contains:
16 dir/symlink pairs
254-char escape symlink
'escape' symlink to /
payload file"]
B --> C["Target extracts with
tarfile.extractall(filter='data')"]
C --> D{"Python resolves symlinks
via os.path.realpath()"}
D --> E["Follows short symlinks aβddd...d
Resolved path grows with each step"]
E --> F{"Resolved path length
> PATH_MAX (4096)?"}
F -->|"No (normal)"| G["realpath correctly resolves
Symlink blocked by filter β"]
F -->|"Yes (overflow!)"| H["realpath STOPS resolving
Falls back to string manipulation"]
H --> I["Python thinks symlink
resolves INSIDE extraction dir"]
I --> J["Filter PASSES the symlink β"]
J --> K["OS follows symlink correctly
'escape' resolves to /"]
K --> L["Payload written to
arbitrary file on disk"]
style F fill:#ff6b6b,color:#fff
style H fill:#ff6b6b,color:#fff
style J fill:#ff6b6b,color:#fff
style L fill:#ff6b6b,color:#fff
style G fill:#51cf66,color:#fff
```
## Attack Sequence
```mermaid
sequenceDiagram
participant A as Attacker Machine
participant T as Target Machine
Note over A: Phase 1 β Preparation
A->>A: Generate SSH keypair (ssh-keygen)
A->>A: Configure exploit variables(DEST_DIR, DEPTH_TO_ROOT, etc.)
A->>A: Run CVE-2025-4517.py or .goto generate malicious tar
Note over A,T: Phase 2 β Delivery
A->>T: Transfer malicious tar to target(scp, wget, curl, ftp, etc.)
T->>T: Place tar in location accessibleto the vulnerable script
Note over T: Phase 3 β Exploitation
T->>T: Trigger extraction via thevulnerable Python script
T->>T: Python calls tarfile.extractall(filter="data")
Note over T: What Python sees vs reality
T->>T: realpath() overflows at PATH_MAX
T->>T: Filter thinks "escape" symlink is safe
T->>T: OS follows "escape" β resolves to /
T->>T: Payload written to /root/.ssh/authorized_keys
Note over A,T: Phase 4 β Access
A->>T: SSH as root using the written key
T-->>A: Root shell obtained
```
## Tar Archive Structure
```mermaid
graph LR
subgraph "Tar Members (extracted in order)"
D1["π ddd...d/"] --> S1["π a β ddd...d"]
D2["π ddd...d/ddd...d/"] --> S2["π ddd...d/b β ddd...d"]
D3["π ...16 levels..."] --> S3["π .../p β ddd...d"]
S4["π a/b/.../p/lll...254...lβ ../../ Γ 16"]
S5["π escapeβ a/b/.../p/lll...l/../../ Γ DEPTH"]
F1["π escape/root/.ssh/authorized_keys(payload content)"]
end
S1 -.->|"short pathstays small"| S2
S2 -.-> S3
S3 -.-> S4
S4 -.->|"254 chars pushespast PATH_MAX"| S5
S5 -.->|"resolves to /"| F1
```
## Usage
### Configuration
Both scripts have a configuration section at the top with these variables:
| Variable | Description | Example |
|----------|-------------|---------|
| `DEST_DIR` | Full path to the extraction directory on the target | `/opt/app/staging/restore_pwn/` |
| `DEPTH_TO_ROOT` | Number of directories from `/` to `DEST_DIR` | `4` for `/opt/app/staging/dir/` |
| `TARGET_FILE` | File to write, relative to `/` | `root/.ssh/authorized_keys` |
| `PAYLOAD` | Content to write into the target file | Your SSH public key |
| `OUTPUT` | Output tar filename | Must match the target's expected pattern |
### Attacker Machine
**Option A: Python**
```bash
# 1. Generate SSH keypair
ssh-keygen -t ed25519 -f root_key -N ''
# 2. Edit CVE-2025-4517.py β update DEST_DIR, DEPTH_TO_ROOT, PAYLOAD, OUTPUT
# 3. Generate the malicious tar
python3 CVE-2025-4517.py
# 4. Transfer to target
scp backup_99.tar user@target:/path/to/backups/
```
**Option B: Go**
```bash
# 1. Generate SSH keypair
ssh-keygen -t ed25519 -f root_key -N ''
# 2. Edit CVE-2025-4517.go β update destDir, depthToRoot, payload, output
# 3. Generate the malicious tar
go run CVE-2025-4517.go
# 4. Transfer to target
scp backup_99.tar user@target:/path/to/backups/
```
### Target Machine
```bash
# Trigger extraction via the vulnerable Python script
# The exact command depends on how the target script is invoked
# Example:
sudo /usr/bin/python3 /path/to/vulnerable_script.py --backup backup_99.tar --restore restore_pwn
```
### Post-Exploitation
```bash
# SSH as root using the planted key
ssh -i root_key root@target
```
## Calculating DEPTH_TO_ROOT
Count the number of directories from `/` to your extraction path:
```
/opt/backup_clients/restored_backups/restore_pwn/
(1) (2) (3) (4)
DEPTH_TO_ROOT = 4
```
```
/tmp/extract/
(1) (2)
DEPTH_TO_ROOT = 2
```
```
/var/lib/app/data/staging/restore_test/
(1) (2) (3) (4) (5) (6)
DEPTH_TO_ROOT = 6
```
## Vulnerable Code Pattern
Any Python script using `tarfile.extractall()` with `filter="data"` on an affected version is potentially exploitable:
```python
import tarfile
with tarfile.open("archive.tar", "r") as tar:
tar.extractall(path="/some/directory", filter="data") # VULNERABLE
```
The `filter="data"` was introduced as a security measure to prevent tar path traversal attacks. Ironically, the vulnerability exists in the very mechanism (`os.path.realpath`) that the filter relies on to validate symlink targets.
## Mitigation
- **Upgrade Python** to 3.12.11+, 3.13.4+, or 3.14.0b1+
- **Avoid extracting untrusted tar archives** regardless of filter settings
- **Use additional validation** on extracted file paths after extraction
## References
- [CVE-2025-4517 - NVD](https://vulners.com/cve/CVE-2025-4517)
- [CVE-2025-4330 - Wiz Vulnerability Database](https://www.wiz.io/vulnerability-database/cve/cve-2025-4330)
- [GHSA-6r6c-684h-9j7p - GitHub Advisory](https://github.com/advisories/GHSA-6r6c-684h-9j7p)
- [CPython PR #135037 - Fix](https://github.com/python/cpython/pull/135037)
## Disclaimer
This tool is provided for **authorized security testing, educational purposes, and research only**. Only use this against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool.
## License
[MIT](LICENSE)