
CVE-2025-4138 / CVE-2025-4517 — Bypass del filtro symlink PATH_MAX di Python tarfile
Scrittura arbitraria di file tramite estrazione con filter="data" / filter="tar"
Una vulnerabilità critica nel modulo tarfile di Python consente a un attaccante di bypassare i filtri di estrazione ("data" e "tar") e scrivere file arbitrari al di fuori della directory di estrazione prevista. Quando un processo privilegiato (ad esempio uno script di backup root, una pipeline CI/CD o un programma di installazione) estrae un archivio tar controllato dall'attaccante utilizzando il parametro apparentemente sicuro filter="data", questo exploit raggiunge la scrittura arbitraria completa di file come quello utente privilegiato — portando tipicamente a privilegi di root.
La causa principale è una peculiarità comportamentale di os.path.realpath(): smette silenziosamente di risolvere i symlink una volta che il percorso completamente espanso supera PATH_MAX (4096 byte su Linux, 1024 su macOS). Il filtro di tarfile si basa su realpath() per i controlli di sicurezza, ma il kernel risolve i symlink indipendentemente durante l'estrazione — creando una finestra TOCTOU (Time-of-Check-to-Time-of-Use) che permette la fuga dalla directory.
| Campo | Valore |
|---|---|
| ID CVE | CVE-2025-4138, CVE-2025-4517 |
| CVSS v3.1 | 9.4 (Critico) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L |
| CWE | CWE-22 — Limitazione impropria di un nome di percorso a una directory ristretta |
| Tipo di vulnerabilità | Path Traversal tramite symlink / bypass del filtro |
| Impatto | Scrittura arbitraria di file → escalation di privilegi, fuga dal sandbox, manomissione dei dati |
| Vettore di attacco | Consegna di un archivio tar malevolo a qualsiasi applicazione che utilizza tarfile.extractall() con filtri |
| Versioni affette | Python 3.12.0 – 3.12.10, 3.13.0 – 3.13.3 |
| Corretto in | Python 3.9.23, 3.10.18, 3.11.13, 3.12.11, 3.13.4 |
| Patch | CPython PR #135037 |
| Avviso | GHSA-hgqp-3mmf-7h8f |
| Segnalato da | Caleb Brown — Google Security Research |
┌───────────────────────────────────────────┐
│ Malicious Tar Structure │
└───────────────────────────────────────────┘
Stage 1 ── Build symlink chain that inflates the resolved path past PATH_MAX
ddd...ddd/ (directory, 247 chars)
a → ddd...ddd (symlink, 1 char name → 247 char dir)
ddd...ddd/ddd...ddd/ (nested directory)
b → ddd...ddd (symlink)
... ×16 levels
Short path (symlinks): a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p ~31 chars
Resolved path (dirs): ddd…/ddd…/ddd…/ddd…/ddd…/ddd…/… ~3968 chars
↑ nearing PATH_MAX
Stage 2 ── Final symlink exceeds PATH_MAX → realpath() stops resolving
a/b/c/…/p/lll…lll → ../../../../../../../../../../../../../../../../..
(16 levels of ".." — traverses back to extraction root)
┌─────────────────────────────────────────────────────────────────┐
│ os.path.realpath() CANNOT expand this → filter says "OK" ✓ │
│ Linux kernel DOES follow chain → actually escapes ✗ │
└─────────────────────────────────────────────────────────────────┘
Stage 3 ── Escape symlink resolves to arbitrary filesystem path
escape → <overflow_link>/../../../../../../../root
Stage 4 ── Create intermediate directories through the escape
escape/.ssh/ (directory, mode 0700 — created by tar extraction)
Stage 5 ── Write payload through the escaped symlink
escape/.ssh/authorized_keys → writes to /root/.ssh/authorized_keys 🔓
---
## Versioni interessate
| Ramo Python | Intervallo vulnerabile | Versione corretta | Stato |
|:--|:--|:--|:--|
| 3.13 | 3.13.0 – 3.13.3 | **3.13.4** | ✅ Corretto |
| 3.12 | 3.12.0 – 3.12.10 | **3.12.11** | ✅ Corretto |
| 3.11 | 3.11.4 – 3.11.12 | **3.11.13** | ✅ Corretto |
| 3.10 | 3.10.12 – 3.10.17 | **3.10.18** | ✅ Corretto |
| 3.9 | 3.9.17 – 3.9.22 | **3.9.23** | ✅ Corretto |
| 3.8 | 3.8.17 – 3.8.20 | — | ❌ Fine vita |
| 3.14+ | Il filtro predefinito è cambiato a `"data"` | Controlla l'ultima versione | ⚠️ Esposizione maggiore |
> **Nota:** Python 3.14+ ha cambiato il parametro predefinito `filter` da nessun filtro a `"data"`, il che significa che le applicazioni che in precedenza non avevano alcun filtro (e quindi erano già insicure) ora usano il filtro vulnerabile per impostazione predefinita.
---
## Schema di codice interessato
Qualsiasi applicazione che esegua questa operazione su una versione vulnerabile di Python è sfruttabile:```python
import tarfile