CVE-2026-128138nltk Command Injection
NVD
Vulnerability Summary
NLTK 3.10.3 incomplete fix: additional JVM/JAR sandbox bypass through unvalidated launcher tokens in `java()` command arguments Summary NLTK 3.10.3 contains security hardening intended to close JVM argument-injection and untrusted JAR/classpath execution paths in `nltk.internals.java()`, including the issue published as GHSA-m4rf-3fr8-xwx3. The patched `options` channel correctly rejects Java argument-file references such as `@/path/to/args` and launcher modes such as `-jar`. However, an additional argument channel remains: the caller-provided `cmd` sequence is appended to the Java command after the validated option and classpath channels, without enforcing that its first item is a Java main-class name. I am reporting this as an additional post-fix bypass / incomplete-fix path affecting the 3.10.3 remediation, rather than as an unrelated vulnerability family. As a result, the same launcher token that is rejected through `options` can be supplied as the first `cmd` item. I confirmed two reachable forms against the official `nltk-3.10.3-py3-none-any.whl`: 1. direct use of the public `nltk.internals.java()` helper with `cmd=["-jar", ...]`; and 2. the higher-level `StanfordSegmenter`, whose public `java_class` constructor argument becomes the first `cmd` item and accepts an `@argfile` launcher token. Both paths execute an external researcher-controlled JAR without setting `NLTK_ALLOW_UNSAFE_JARS`, `NLTK_ALLOW_UNSAFE_CLASSPATH`, or using `trusted_raw_options`. The original explicit outside-classpath route is blocked in 3.10.3, while the alternate command channel executes the JAR 3/3 times. Affected artifact ```text Package: nltk Confirmed affected version: 3.10.3 Wheel: nltk-3.10.3-py3-none-any.whl SHA-256: ff9598a8e20518ee0d557745890cc4435b9578489e2dcbc69c4f81fa060caf7c ``` Baseline artifact: ```text Version: 3.10.2 Wheel: nltk-3.10.2-py3-none-any.whl SHA-256: 2c7ccacb765c5e26b0cb60fb1b57080af522c6924d12a714a243305ba3637412 ``` Relationship to the 3.10.3 security fixes The existing 3.10.3 hardening validates the dedicated per-call `options` list before it reaches `subprocess.Popen`. In particular, the exact `@argfile` token used in this report is rejected through `options` with a `ValueError` that references CVE-2026-12841 and CWE-88. The command is subsequently assembled in this order: ```python final_cmd.extend(validated_options) if classpath_arg is not None: final_cmd.extend(["-cp", classpath_arg]) final_cmd.extend(cmd_list) ``` `cmd_list` is copied from the public `cmd` argument but its first token is not checked to ensure that it is an ordinary Java main class. Java therefore interprets leading `-jar`, `@argfile`, module-mode, or classpath-mode tokens as launcher control arguments rather than application arguments. This is an alternate argument channel around the same JVM argument/JAR execution boundary protected by the 3.10.3 changes. Confirmed API paths Direct public helper ```python nltk.internals.java( ["-jar", "/outside/trusted/roots/controlled.jar"], classpath=None, stdout="pipe", stderr="pipe", ) ``` The external JAR executed in all three subject runs. Higher-level `StanfordSegmenter` path `StanfordSegmenter` exposes a public `java_class` constructor argument, stores it, and later places it at the beginning of the `cmd` sequence passed to `nltk.internals.java()`. A controlled argument file containing a `-jar` launcher invocation was supplied as: ```python StanfordSegmenter( path_to_jar=trusted_segmenter_jar, java_class="@/path/to/controlled-launcher.args", path_to_model="dummy-model", java_options=[], ) ``` The trusted segmenter JAR passed the wrapper's checksum and classpath checks. The `@argfile` then switched the Java launcher to the external JAR, which executed in all three runs. The deprecated wrapper subsequently raised an unrelated post-launch `AttributeError` while processing its output. The external marker had already been written by the child JVM before that exception. Proof matrix ```text R1 NLTK 3.10.2 + explicit external classpath -> controlled Java bytecode executed R2 NLTK 3.10.3 + same explicit external classpath -> UntrustedJarError; no marker R3A NLTK 3.10.3 + direct cmd=["-jar", external_jar] -> controlled external JAR executed, 3/3 R3B NLTK 3.10.3 + StanfordSegmenter(java_class="@argfile") -> controlled external JAR executed, 3/3 R4 NLTK 3.10.3 + ordinary main class in trusted classpath -> legitimate execution preserved ``` A direct channel comparison also confirmed: ```text options=["@argfile"] -> BLOCKED cmd/java_class="@argfile" -> EXTERNAL JAR EXECUTED ``` Machine-readable proof results and a portable reproducer are attached. Preconditions Practical exploitability depends on an embedding application exposing attacker influence over one of these public argument paths: - the leading item of `nltk.internals.java(cmd=...)`; or - `StanfordSegmenter(java_class=...)` or an equivalent wrapper-controlled command token. The attacker must also be able to reference an argument file or JAR readable by the NLTK process. The Java child process executes with the privileges of that process. This is not a claim that importing NLTK alone is remotely exploitable. The confirmed issue is that the default protected execution boundary can be bypassed when these argument channels are exposed to untrusted input. Impact Successful exploitation allows execution of attacker-selected Java bytecode with the privileges of the process using NLTK. Suggested classifications: - CWE-88: Improper Neutralization of Argument Delimiters in a Command - CWE-94: Improper Control of Generation of Code Suggested severity: Moderate, subject to maintainer assessment of realistic argument reachability in embedding applications. Reproduction The complete reproducer, machine-readable proof results, and proposed remediation are included in the attached evidence package. nltk-3103-candidate-a-maintainer-evidence.zip The included `reproducer/reproduce_cmd_channel_bypass.py` creates only temporary researcher-controlled Java source, JAR, argument-file, and marker artifacts. It performs no network access. Requirements: - Python 3.10 or newer - `nltk==3.10.3` - a JDK providing `java`, `javac`, and `jar` Run: ```bash python reproducer/reproduce_cmd_channel_bypass.py ``` A confirmed run exits with status 0 and reports that: - the `options` channel blocks `@argfile`; - the direct `cmd` channel executes the external JAR 3/3; - the higher-level `StanfordSegmenter.java_class` channel executes it 3/3; and - legitimate trusted main-class execution remains functional. Recommended remediation Enforce that the first ordinary `cmd` item is a Java main-class token. Reject Java launcher switches and `@argfile` references in this channel by default, before constructing or executing the subprocess command. For example: ```python cmd_list = list(cmd) if not cmd_list: raise ValueError("cmd must contain a Java main class") first = cmd_list[0] if not isinstance(first, str) or not first or first.startswith(("-", "@")): raise ValueError("cmd must begin with a Java main class") ``` If raw Java launcher modes are intentionally supported, they should require a clearly named explicit trusted escape hatch rather than sharing the normal main-class channel. The attachment includes a proposed source patch and focused regression tests. Local verification against code derived from the official 3.10.3 wheel showed: ```text direct -jar command channel BLOCKED StanfordSegmenter @argfile path BLOCKED external marker creation BLOCKED legitimate trusted main class PRESERVED ``` Relationship to existing advisory / duplicate note The public advisory GHSA-m4rf-3fr8-xwx3 describes the per-call `options` validation gap fixed in 3.10.3. This report is intentionally scoped as an additional bypass of that remediation through the distinct `cmd`/`java_class` argument channel. I did not locate a public advisory, issue, or pull request describing this leading-`cmd` launcher-token path. I cannot determine whether an equivalent private report already exists, so please deduplicate this report against any private reports or unpublished follow-up work. Please keep this report and attachment private while assessing and preparing any fix. I am available to test a candidate patch and adapt the regression tests to the project's preferred style. Fix status (2026-09-28) Status: MERGED on develop (unreleased; v3.10.3 of 2026-08-12 predates the fix). PR: #3771 "Reject Java launcher switches and @argfiles in the java() cmd argument (CWE-88)", merged 2026-08-20, merge commit 45a08fded on develop. Fix commit: b36212795 (java() raises ValueError when the first cmd token is a launcher switch or an @argfile, and when any cmd token is an @argfile; StanfordSegmenter passes its java_class as that first token, nltk/tokenize/stanford_segmenter.py line 432, so both reported paths hit the same check). Probe: none. Reproduction on develop 2fa3f48fe with the process spawn intercepted: cmd=["-jar", jar], cmd=["@argfile"] and cmd=["Main", "@argfile"] each raise ValueError (CWE-88) before any spawn. Release: none yet; no release tag contains b36212795.
CVSS v3.1 Base Metrics — Score 9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HAttack VectorNetwork
Attack ComplexityLow
Privileges RequiredNone
User InteractionNone
ScopeUnchanged
ConfidentialityHigh
IntegrityHigh
AvailabilityHigh
Affected & Patched Versions
- nltk 3.10.3
- nltk 3.10.4
What to do
Plan remediation soon. No exploitation is confirmed yet, but the severity is critical.
- Check whether you run nltk and which version.
- Update to a patched version listed above, then confirm the new version.
Timeline
- Nov 15, 2026Published
- Nov 15, 2026Record last modified
Score, severity and exploitation changes are tracked from the day Watchtower started logging them.
Get notified about new nltk CVEsYou are reading CVE-2026-128138. Get an email when KEV listings, exploits or new CVEs match your stack.
Set up CVE alertsMore nltk CVEs
External References
Data last checked: Nov 15, 2026 17:01 UTC