Every CVE Search/ Get API key

CVE-2026-109956

NLTK through 3.10.3 Path Traversal via WekaClassifier Model Path

8.3 HighFix available

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
High
Availability
Low

Exploit likelihood

0.32%Unchanged since Oct 14, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.3

NLTK through 3.10.3 Path Traversal via WekaClassifier Model Path

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
CVE-2026-109956
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-98660 8.3

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
EUVD-2026-98660
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-9fgx-q9j8-3j83 8.3

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows...

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
GHSA-9fgx-q9j8-3j83
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L
Weakness
CWE-22
Updated
2026-11-02
GitHub AdvisoriesGHSA-j456-xh4h-cpf2 8.6

NLTK: WekaClassifier passes an unvalidated caller-controlled model path to the weka JVM, bypassing nltk.pathsec containment

Summary

WekaClassifier in NLTK passes a caller-controlled model file path to the weka Java VM without any nltk.pathsec validation. WekaClassifier.train puts the caller's model_filename verbatim into the weka argv ["-d", model_filename, ...] (weka writes the trained model there), and WekaClassifier._classify_many puts self._model into ["-l", self._model, ...] (weka reads it). Under nltk.pathsec.ENFORCE=True (the default on current HEAD), NLTK's stated invariant — established by advisory CVE-2026-81726 / GHSA-8mgp-746c-j5xp — is that caller-controlled model-artifact paths must be bounded to the allowed NLTK data roots before any native tool opens them. WekaClassifier is the one java()-based tool wrapper that skips this entirely, giving an untrusted workflow arbitrary-location file write (-d) and an arbitrary file read/parse oracle (-l), defeating pathsec containment.

This is a standalone, unfixed variant of the same class as CVE-2026-81726, on a code path the CVE-2026-81726 fix never touched. That advisory enumerates 6 functions (TransitionParser.train, TransitionParser.parse, AveragedPerceptron.save, AveragedPerceptron.load, PerceptronTagger.save_to_json, save_maxent_params) — WekaClassifier is not among them, and weka.py contains no validate_tool_path/validate_model_resource at all.

Root Cause

WekaClassifier.train(cls, model_filename, ...) builds cmd = [javaclass, "-d", model_filename, "-t", train_filename] (nltk/classify/weka.py:256) and WekaClassifier._classify_many builds [..., "-l", self._model, "-T", <staged>, ...] (weka.py:131-133); self._model is stored unvalidated in __init__ (weka.py:108-110). Only the internally-generated train.arff/test.arff are staged via make_staging_dir; the model path is passed as-is. nltk.internals.java() (internals.py:388-431) validates the classpath (_verify_jar_sandbox), the JVM launcher options (_validate_java_options, the CVE-2026-12841 fix), the main-class token cmd[0], and rejects @argfile tokens in any position — but it explicitly permits program arguments starting with - and never inspects the interior of a program-arg path value. So -d <path> / -l <path> flow unchecked to subprocess.Popen(final_cmd, shell=False) and the weka JVM performs an unbounded native open/write on the caller-chosen path.

Sibling wrappers prove the intended pattern: CRFTagger.train/set_model_file call validate_tool_path (crf.py:134,300); MaltParser.generate_malt_command validates -i/-o via validate_tool_path (malt.py:340,344), its model via validate_model_resource (malt.py:326) and working_dir via validate_path (malt.py:206). weka.py already uses pathsec elsewhere (validate_path for WEKAHOME, pathsec_open for ARFF writes) but never for the model path.

Impact

Under pathsec.ENFORCE=True (default on HEAD), an untrusted workflow that chooses the weka model save/load path obtains:

  • Arbitrary-location file write via -d — overwrite or create a file at an attacker-chosen absolute or ..-traversal path outside the allowed NLTK data roots (content constrained to weka's serialized-model format; the target choice is the integrity impact). This corrupts/clobbers arbitrary chosen files.
  • Arbitrary file read/parse oracle via -l — weka loads an out-of-root path as a model, yielding an existence/parse differential (file content is not echoed back → confidentiality impact is limited).

This defeats the pathsec containment advisory CVE-2026-81726 promises, at the same privilege level / threat model that advisory was accepted under.

Proof of Concept

import nltk.pathsec as ps; ps.ENFORCE = True
from nltk.classify.weka import WekaClassifier
# write-side: model written OUTSIDE any allowed NLTK data root (refused for CRFTagger, allowed here)
WekaClassifier.train("/tmp/outside_sandbox/evil.model",
                     [({"a": 1}, "pos"), ({"a": 0}, "neg")])
#…
Record
GHSA-j456-xh4h-cpf2
CVSS 3.1
8.6 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L
Weakness
CWE-22, CWE-73
Updated
2026-11-02
NVD 8.3

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
CVE-2026-109956
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L
Weakness
CWE-22
Updated
2026-11-02
OSV 8.3

NLTK through 3.10.3 Path Traversal via WekaClassifier Model Path

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
CVE-2026-109956
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVDEBIAN-CVE-2026-109956 8.3

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
DEBIAN-CVE-2026-109956
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Updated
2026-11-02
OSVGHSA-j456-xh4h-cpf2 8.6

NLTK: WekaClassifier passes an unvalidated caller-controlled model path to the weka JVM, bypassing nltk.pathsec containment

Summary

WekaClassifier in NLTK passes a caller-controlled model file path to the weka Java VM without any nltk.pathsec validation. WekaClassifier.train puts the caller's model_filename verbatim into the weka argv ["-d", model_filename, ...] (weka writes the trained model there), and WekaClassifier._classify_many puts self._model into ["-l", self._model, ...] (weka reads it). Under nltk.pathsec.ENFORCE=True (the default on current HEAD), NLTK's stated invariant — established by advisory CVE-2026-81726 / GHSA-8mgp-746c-j5xp — is that caller-controlled model-artifact paths must be bounded to the allowed NLTK data roots before any native tool opens them. WekaClassifier is the one java()-based tool wrapper that skips this entirely, giving an untrusted workflow arbitrary-location file write (-d) and an arbitrary file read/parse oracle (-l), defeating pathsec containment.

This is a standalone, unfixed variant of the same class as CVE-2026-81726, on a code path the CVE-2026-81726 fix never touched. That advisory enumerates 6 functions (TransitionParser.train, TransitionParser.parse, AveragedPerceptron.save, AveragedPerceptron.load, PerceptronTagger.save_to_json, save_maxent_params) — WekaClassifier is not among them, and weka.py contains no validate_tool_path/validate_model_resource at all.

Root Cause

WekaClassifier.train(cls, model_filename, ...) builds cmd = [javaclass, "-d", model_filename, "-t", train_filename] (nltk/classify/weka.py:256) and WekaClassifier._classify_many builds [..., "-l", self._model, "-T", <staged>, ...] (weka.py:131-133); self._model is stored unvalidated in __init__ (weka.py:108-110). Only the internally-generated train.arff/test.arff are staged via make_staging_dir; the model path is passed as-is. nltk.internals.java() (internals.py:388-431) validates the classpath (_verify_jar_sandbox), the JVM launcher options (_validate_java_options, the CVE-2026-12841 fix), the main-class token cmd[0], and rejects @argfile tokens in any position — but it explicitly permits program arguments starting with - and never inspects the interior of a program-arg path value. So -d <path> / -l <path> flow unchecked to subprocess.Popen(final_cmd, shell=False) and the weka JVM performs an unbounded native open/write on the caller-chosen path.

Sibling wrappers prove the intended pattern: CRFTagger.train/set_model_file call validate_tool_path (crf.py:134,300); MaltParser.generate_malt_command validates -i/-o via validate_tool_path (malt.py:340,344), its model via validate_model_resource (malt.py:326) and working_dir via validate_path (malt.py:206). weka.py already uses pathsec elsewhere (validate_path for WEKAHOME, pathsec_open for ARFF writes) but never for the model path.

Impact

Under pathsec.ENFORCE=True (default on HEAD), an untrusted workflow that chooses the weka model save/load path obtains:

  • Arbitrary-location file write via -d — overwrite or create a file at an attacker-chosen absolute or ..-traversal path outside the allowed NLTK data roots (content constrained to weka's serialized-model format; the target choice is the integrity impact). This corrupts/clobbers arbitrary chosen files.
  • Arbitrary file read/parse oracle via -l — weka loads an out-of-root path as a model, yielding an existence/parse differential (file content is not echoed back → confidentiality impact is limited).

This defeats the pathsec containment advisory CVE-2026-81726 promises, at the same privilege level / threat model that advisory was accepted under.

Proof of Concept

import nltk.pathsec as ps; ps.ENFORCE = True
from nltk.classify.weka import WekaClassifier
# write-side: model written OUTSIDE any allowed NLTK data root (refused for CRFTagger, allowed here)
WekaClassifier.train("/tmp/outside_sandbox/evil.model",
                     [({"a": 1}, "pos"), ({"a": 0}, "neg")])
#…
Record
GHSA-j456-xh4h-cpf2
CVSS 3.1
8.6 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L
Weakness
CWE-22, CWE-73
Updated
2026-11-02
OSVUBUNTU-CVE-2026-109956 8.3

NLTK through 3.10.3 contains a path traversal vulnerability in WekaClassifier that allows attackers to bypass nltk.pathsec containment by supplying unvalidated model paths. Attackers controlling the model_filename or model argument can make the weka JVM write files via -d or probe files via -l outside allowed data roots.

Record
UBUNTU-CVE-2026-109956
CVSS 4.0
8.3 HighCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
nltk
PyPI
through 3.10.3
3.10.4
nltk
Debian:12
All versions, plus listed versions
3.10.0-13.10.1-13.10.3-13.8-13.8.1-1
Show 4 moreShow less
3.9.1-13.9.1-23.9.2-13.9.3-1
None known
nltk
Debian:13
All versions, plus listed versions
3.10.0-13.10.1-13.10.3-13.9.1-23.9.2-13.9.3-1
None known
nltk
Debian:14
All versions, plus listed versions
3.10.0-13.10.1-13.10.3-13.9.1-23.9.2-13.9.3-1
None known
nltk
PyPI
before 3.10.4, plus listed versions
0.80.90.9.30.9.40.9.5
Show 45 moreShow less
0.9.60.9.70.9.80.9.92.0.12.0.1rc12.0.1rc2-git2.0.1rc32.0.1rc42.0.22.0.32.0.42.0.52.0b42.0b52.0b62.0b72.0b82.0b93.0.03.0.0b13.0.0b23.0.13.0.23.0.33.0.43.0.53.13.10.03.10.13.10.23.10.33.23.2.13.2.23.2.33.2.43.2.53.33.43.4.13.4.23.4.33.4.43.4.5
3.10.4
nltk
Ubuntu:Pro:14.04:LTS
All versions, plus listed versions
2.0~b9-0ubuntu42.0~b9-0ubuntu4.1~esm22.0~b9-0ubuntu4.1~esm42.0~b9-0ubuntu4.1~esm52.0~b9-0ubuntu4.1~esm6
None known
nltk
Ubuntu:Pro:16.04:LTS
All versions, plus listed versions
3.0.4-13.0.5-13.1-13.1-1ubuntu0.13.1-1ubuntu0.1+esm13.1-1ubuntu0.1+esm23.1-1ubuntu0.1+esm33.1-1ubuntu0.1+esm4
None known
nltk
Ubuntu:Pro:18.04:LTS
All versions, plus listed versions
3.2.4-13.2.5-13.2.5-1ubuntu0.13.2.5-1ubuntu0.1+esm13.2.5-1ubuntu0.1+esm23.2.5-1ubuntu0.1+esm33.2.5-1ubuntu0.1+esm4
None known
nltk
Ubuntu:Pro:20.04:LTS
All versions, plus listed versions
3.4.5-13.4.5-23.4.5-2ubuntu0.1~esm13.4.5-2ubuntu0.1~esm23.4.5-2ubuntu0.1~esm33.4.5-2ubuntu0.1~esm4
None known
nltk
Ubuntu:Pro:22.04:LTS
All versions, plus listed versions
3.5-13.6.5-13.6.7-13.7-13.7-1ubuntu0.1~esm13.7-1ubuntu0.1~esm2
None known
nltk
Ubuntu:Pro:24.04:LTS
All versions, plus listed versions
3.8.1-13.8.1-1ubuntu0.1~esm13.8.1-1ubuntu0.1~esm2
None known
nltk
Ubuntu:26.10
All versions, plus listed versions
3.9.2-13.9.3-13.10.0-13.10.1-13.10.3-1
None known
nltk
Ubuntu:Pro:26.04:LTS
All versions, plus listed versions
3.9.1-23.9.1-2build13.9.2-13.9.2-1ubuntu0.1~esm13.9.2-1ubuntu0.1~esm2
None known

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-109956 is a high vulnerability (CVSS 8.3) in Nltk, published October 13, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.32%, the 24th percentile. Fixed in nltk 3.10.4.

References

Show 7 moreShow less
This page doesn't list everything

Left out here: 19 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open