NLTK WekaClassifier 路徑遍歷及沙盒繞過漏洞(CVE-2026-109956)
GHSA-J456-XH4H-CPF2: CVE-2026-109956: Path Traversal and Sandbox Bypass in NLTK WekaClassifier
NLTK 3.10.3 及更早版本的 WekaClassifier 未有驗證由呼叫方控制的模型檔案路徑,可繞過 nltk.pathsec 沙盒,造成任意檔案覆寫及本機檔案探測。漏洞 CVSS v3.1 評分為 7.0,已於 NLTK 3.10.4 修復;建議升級至 3.10.4 或以上版本。
CVE-2026-109956: Path Traversal and Sandbox Bypass in NLTK WekaClassifier
Vulnerability ID: GHSA-J456-XH4H-CPF2
CVSS Score: 7.0
Published: 2026-10-27
CVE-2026-109956 (GHSA-j456-xh4h-cpf2) is a path traversal vulnerability and security sandbox bypass in the Python Natural Language Toolkit (NLTK) versions 3.10.3 and earlier. The WekaClassifier module fails to validate caller-controlled model file paths before passing them to the Weka Java Virtual Machine (JVM) via command-line arguments. When operating under the nltk.pathsec security sandbox, this omission permits attackers who control model path parameters to read, probe, or overwrite arbitrary files outside authorized NLTK data directories.
TL;DR
NLTK versions up to 3.10.3 fail to validate model file paths in WekaClassifier.train() and WekaClassifier._classify_many(), bypassing the nltk.pathsec filesystem sandbox and enabling arbitrary file overwrites and local file existence probing via the Weka JVM.
⚠️ Exploit Status: POC
Technical Details
- Vulnerability Class: CWE-22: Path Traversal / Sandbox Bypass
- Affected Component: nltk.classify.weka.WekaClassifier (PyPI: nltk <= 3.10.3)
- CVSS v3.1 / v4.0: 7.0 High (NVD v3.1) / 8.3 High (VulnCheck v4.0)
- Attack Vector: Network / Caller-Controlled Model Path Argument
- EPSS Score: 0.32% (24.59th Percentile)
- Security Impact: Arbitrary File Overwrite (-d) and Local File Probing (-l)
- Exploit Status: Proof-of-Concept (Unit Test Suite Verified)
- CISA KEV Status: Not Listed
Affected Systems
- Python Natural Language Toolkit (nltk) <= 3.10.3
-
nltk: <= 3.10.3 (Fixed in:
3.10.4)
Code Analysis
Commit: 6a17606
Bound the WekaClassifier model path to the pathsec data roots (GHSA-j456-xh4h-cpf2)
@@ -22,7 +22,7 @@
from nltk.internals import config_java, java
from nltk.pathsec import ZipFile as SecureZipFile
from nltk.pathsec import open as pathsec_open
-from nltk.pathsec import validate_path
+from nltk.pathsec import validate_path, validate_tool_path
from nltk.probability import DictionaryProbDist
_weka_classpath = None
@@ -107,6 +107,10 @@ def _check_weka_version(jar):
class WekaClassifier(ClassifierI):
def __init__(self, formatter, model_filename):
self._formatter = formatter
+ # Bound the caller-controlled model path to the pathsec data roots before
+ # it reaches the weka JVM as a -l read target, closing the model-artifact
+ # containment gap weka.py was left out of (GHSA-j456-xh4h-cpf2).
+ validate_tool_path(model_filename, context="WekaClassifier", must_exist=False)
self._model = model_filename
def prob_classify_many(self, featuresets):
@@ -116,6 +120,12 @@ def classify_many(self, featuresets):
return self._classify_many(featuresets, ["-p", "0"])
def _classify_many(self, featuresets, options):
+ # Re-bound the model path in case _model was reassigned after construction;
+ # weka reads it via -l (GHSA-j456-xh4h-cpf2). Containment, not existence, is
+ # the security property, so must_exist stays False.
+ validate_tool_path(
+ self._model, context="WekaClassifier._classify_many", must_exist=False
+ )
# Make sure we can find java & weka.
config_weka()
@@ -233,6 +243,15 @@ def train(
options=[],
quiet=True,
):
+ # Bound the caller-controlled model path before weka writes to it via -d
+ # (GHSA-j456-xh4h-cpf2); it need not exist yet, and validating before any
+ # weka lookup refuses an out-of-root path early.
+ validate_tool_path(
+ model_filename,
+ context="WekaClassifier.train",
+ for_write=True,
+ must_exist=False,
+ )
# Make sure we can find java & weka.
config_weka()
Exploit Details
- GitHub: Pull Request #3863 containing unit tests (test_weka_model_path_security.py) and proof-of-concept payloads demonstrating out-of-root writes, reads, and symlink escapes.
- GitHub Advisory: Official GitHub Security Advisory detailing the WekaClassifier -d and -l pathsec bypass mechanics.
Mitigation Strategies
- Upgrade the nltk package to version 3.10.4 or later to enforce pathsec validation on WekaClassifier model paths.
- Wrap calls to WekaClassifier and WekaClassifier.train() with explicit nltk.pathsec.validate_tool_path() checks in application code if running on legacy NLTK versions.
- Remove weka.jar and unset the WEKAHOME environment variable on systems that do not require Java-based Weka classification.
- Run Python NLP workloads under least-privilege OS service accounts with read-only access to system and application code directories.
Remediation Steps:
- Audit project dependencies (requirements.txt, Pipfile, poetry.lock) to identify nltk versions <= 3.10.3.
- Update nltk to >= 3.10.4 using pip: pip install --upgrade 'nltk>=3.10.4'.
- Verify that nltk.pathsec.ENFORCE is set to True across production environments.
- Run unit tests to confirm that any custom Weka model paths reside within authorized nltk.data.path directories.
References
- GitHub Security Advisory GHSA-j456-xh4h-cpf2
- CVE-2026-109956 Record
- NVD - CVE-2026-109956 Detail
- NLTK Pull Request #3863: Bound the WekaClassifier model path to the pathsec data roots
- GitHub Fix Commit 6a17606293fe9d3ea1006f99dd3fed2e2b25d519
- NLTK Release v3.10.4
- VulnCheck Advisory: NLTK through 3.10.3 Path Traversal via WekaClassifier Model Path
- Red Hat Security Advisory for CVE-2026-109956
- Red Hat Bugzilla #2550515 - CVE-2026-109956
Read the full report for GHSA-J456-XH4H-CPF2 on our website for more details including interactive diagrams and full exploit analysis.
來源:DEV Community · dev.to