Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-60455

CVE-2025-60455: Modular Max Serve RCE Vulnerability

CVE-2025-60455 is an unsafe deserialization flaw in Modular Max Serve that enables remote code execution when experimental KV cache agent is enabled. This article covers technical details, affected versions, and mitigations.

Published:

CVE-2025-60455 Overview

CVE-2025-60455 is an unsafe deserialization vulnerability in Modular Max Serve versions before 25.6. The flaw exists in the KV Cache Agent component when the --experimental-enable-kvcache-agent feature is enabled. The component used Python pickle deserialization over a ZeroMQ (ZMQ) transport, allowing an attacker who can deliver crafted messages to the agent to execute arbitrary code in the serving process. The issue is tracked as [CWE-502: Deserialization of Untrusted Data]. It was disclosed alongside Oligo Security's ShadowMQ research into pickle-over-ZMQ patterns reused across multiple AI inference frameworks.

Critical Impact

Successful exploitation yields arbitrary code execution in the Max Serve process, compromising confidentiality, integrity, and availability of the AI inference host.

Affected Products

  • Modular Max Serve versions prior to 25.6
  • Deployments running with --experimental-enable-kvcache-agent enabled
  • The max/serve/kvcache_agent/kvcache_agent.py and dispatcher_*.py components

Discovery Timeline

  • 2025-11-18 - CVE-2025-60455 published to NVD
  • 2026-01-08 - Last updated in NVD database

Technical Details for CVE-2025-60455

Vulnerability Analysis

Max Serve's KV Cache Agent provides inter-process messaging between inference workers and a cache coordination service. The agent used pickle to deserialize KVCacheChangeMessage objects delivered through a ZMQ pull socket. Python's pickle module executes arbitrary callables embedded in the serialized stream during __reduce__ resolution, so any controllable message becomes a code execution primitive. Because the listener accepted messages on a ZMQ endpoint without authentication or integrity checks, any process able to reach that endpoint could trigger deserialization. The dispatcher client and service used the same pickle-by-default serialization pattern, broadening the attack surface across the agent's IPC paths.

Root Cause

The root cause is reliance on pickle for cross-process message decoding. A code comment in the pre-patch source acknowledged this directly: pickle was retained because KVCacheChangeMessage carried Protobuf enums that msgspec/msgpack could not encode. That tradeoff prioritized serialization convenience over message safety, leaving the deserializer trust-equivalent to a code loader.

Attack Vector

The attack vector is local (AV:L) and requires no privileges or user interaction. An attacker with local access to the host, or any code able to publish to the KV cache ZMQ endpoint, can send a crafted pickle payload. On deserialization, the embedded gadget executes in the context of the Max Serve process. Containerized or multi-tenant inference deployments where the ZMQ socket is reachable from co-located workloads face elevated risk.

python
# Patch excerpt: kvcache_agent.py removes pickle and switches to msgpack
 import concurrent.futures
 import logging
-import pickle
 import queue
 import threading
 from collections.abc import Iterator
 from dataclasses import dataclass
 from typing import Any

 import grpc
+from max.interfaces import msgpack_numpy_decoder
 from max.serve.kvcache_agent.kvcache_agent_service_v1_pb2 import (
     KVCacheStateUpdate,
     MemoryTier,

Source: GitHub Commit 10620059

python
# Patch excerpt: replaces pickle-backed decoder with msgpack int decoder
         self.config = config
-        # This remains the only use of pickle in the current codebase.
-        # As the KVCacheChangeMessage contains Protobuf Enum's it cannot be
-        # serialized by msgspec/msgpack.
         self._kv_cache_events_pull_socket = ZmqPullSocket[KVCacheChangeMessage](
             zmq_endpoint=kv_cache_events_zmq_endpoint,
-            deserialize=msgpack_numpy_decoder(KVCacheChangeMessage),
+            deserialize=msgpack_numpy_decoder(int),
         )

Source: GitHub Commit b20e749

python
# Patch excerpt: dispatcher_client.py / dispatcher_service.py remove pickle default
         self.pull_socket = ZmqPullSocket[
             DispatcherMessage[DispatcherMessagePayload]
-        ](zmq_ctx, recv_endpoint, deserialize=deserialize)
+        ](zmq_ctx, zmq_endpoint=recv_endpoint, deserialize=deserialize)
         self.push_socket = ZmqPushSocket[
             DispatcherMessage[DispatcherMessagePayload]
-        ](zmq_ctx, send_endpoint, serialize=serialize)
+        ](zmq_ctx, zmq_endpoint=send_endpoint, serialize=serialize)

Source: GitHub Commit ee9c4ab

Detection Methods for CVE-2025-60455

Indicators of Compromise

  • Max Serve processes spawning unexpected child processes such as sh, bash, python, or network utilities.
  • Outbound network connections originating from the Max Serve worker to non-inference destinations.
  • Unexpected writes by the serving user to directories outside the model cache or log paths.
  • Use of the --experimental-enable-kvcache-agent flag on versions earlier than 25.6.

Detection Strategies

  • Inventory running Max Serve instances and flag any started with --experimental-enable-kvcache-agent on a vulnerable version.
  • Inspect process command lines and audit logs for the KV cache agent endpoint binding and the set of peers connecting to it.
  • Apply static checks for import pickle and pickle.loads usage in deployed Max Serve packages to confirm patch state.

Monitoring Recommendations

  • Monitor ZMQ socket activity on the KV cache endpoint and alert on connections from unexpected local users or containers.
  • Forward Max Serve stdout, stderr, and host process telemetry to a centralized log store and alert on new child process lineage.
  • Track package versions of modular:max across hosts and surface any deployment still below 25.6.

How to Mitigate CVE-2025-60455

Immediate Actions Required

  • Upgrade Modular Max to version 25.6 or later, which removes pickle-based deserialization in the KV cache agent and dispatcher.
  • Disable the --experimental-enable-kvcache-agent feature on any host that cannot be patched immediately.
  • Restrict local access to inference hosts and isolate Max Serve workloads from untrusted tenants or sidecar containers.

Patch Information

The fix is delivered across three commits in the modular/modular repository: 10620059 removes the pickle import from kvcache_agent.py and adopts msgpack_numpy_decoder; b20e749 replaces the pickle-backed event decoder with an int msgpack decoder; and ee9c4ab removes pickle default serialization in dispatcher_client.py and dispatcher_service.py. Tracking issue: modular/modular#4795.

Workarounds

  • Do not pass --experimental-enable-kvcache-agent to Max Serve until the host is patched.
  • Bind any required ZMQ endpoints to loopback only and prevent cross-container or cross-tenant reachability.
  • Run Max Serve under a dedicated low-privilege user with restricted filesystem and network egress to contain post-exploitation impact.
bash
# Verify installed version and that the KV cache agent flag is not set
pip show modular-max | grep -i version
ps -ef | grep -i max-serve | grep -- '--experimental-enable-kvcache-agent'

# Confirm pickle has been removed from the deployed agent module
python - <<'EOF'
import inspect, max.serve.kvcache_agent.kvcache_agent as m
print('pickle' in inspect.getsource(m))  # expect: False on 25.6+
EOF

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.