Sep 16, 2026·6 min read·27 visits
Unauthenticated remote attackers can execute arbitrary code on LMDeploy hosts prior to v0.10.2 by sending a malicious Python pickle stream to the dynamically opened ZMQ RPC port.
LMDeploy prior to version 0.10.2 is vulnerable to remote code execution because its AsyncRPCServer component implements unauthenticated, remote-accessible communication sockets and uses the insecure pickle.loads() deserializer to process incoming requests.
LMDeploy is an open-source toolkit designed for compressing, deploying, and serving large language models (LLMs). The toolkit implements an asynchronous Remote Procedure Call (RPC) server, AsyncRPCServer, within lmdeploy/pytorch/engine/mp_engine/zmq_rpc.py to manage inter-process or multi-node tensor-parallel and data-parallel inference orchestration.
During parallel execution, nodes exchange serialization states to synchronize inference workloads. The RPC server exposes a ZeroMQ (ZMQ) router socket to handle this inter-node communication. Prior to version 0.10.2, this socket bound to wildcard network interfaces, exposing the system's unauthenticated message handling functionality to the external network.
The exposed RPC handler processes incoming binary payloads without verification or access control. This architectural trust boundary omission allows any network-adjacent actor with TCP access to the dynamically assigned port to send payloads directly to the underlying processing loop. Consequently, the affected versions of LMDeploy suffer from an unauthenticated remote code execution vulnerability, tracked as CVE-2025-59953.
The root cause of CVE-2025-59953 is the insecure use of Python's native pickle module within the message processing routine of AsyncRPCServer. Specifically, the server invokes pickle.loads() directly on received raw byte frames inside the call_and_response() method. The pickle serialization protocol is designed for trusted environments and possesses no intrinsic capability to distinguish safe data structure definitions from arbitrary executable object instructions.
Python's object deserialization architecture executes the instructions encoded in the serialized stream to reconstruct the serialized object. An attacker can construct a serialized byte stream containing instructions that execute arbitrary shell commands or launch external binary processes. The execution occurs when the class reconstruction machinery processes the __reduce__ or __setstate__ methods defined in the malicious payload.
Because the server executed this deserialization routine on unvalidated network packets, any system with TCP routing to the server port is vulnerable. This enables arbitrary code execution with the permissions of the user context running the LMDeploy RPC worker. This behavior is standard for the Python interpreter but introduces a vulnerability when exposed directly to a network socket.
In affected versions of LMDeploy, the initialization of the AsyncRPCServer established a socket address of tcp://*. This configuration binds the ZMQ ROUTER socket to all available network interfaces on the host operating system. The socket is dynamically assigned a port via bind_to_random_port(), which exposes the unauthenticated deserialization entry point to external network traffic.
The patch in commit d86046a0e6b02ecaaf7cdb74346d0477130221c2 modifies the initialization parameters to restrict binding strictly to the local loopback interface. This limits TCP access to local processes or containers sharing the host's loopback interface. Let us look at the code-level change in the initialization routine.
# Vulnerable configuration in lmdeploy/pytorch/engine/mp_engine/zmq_rpc.py
class AsyncRPCServer:
def __init__(self):
address = 'tcp://*'
self.context = zmq.Context()
self.socket = self.context.socket(zmq.ROUTER)
self.port = self.socket.bind_to_random_port(address)# Patched configuration in lmdeploy/pytorch/engine/mp_engine/zmq_rpc.py
class AsyncRPCServer:
def __init__(self):
# Warning: DO NOT allow visit rpc server from external network
# unauthorized access may lead to code execution vulnerability
address = 'tcp://localhost'
self.context = zmq.Context()
self.socket = self.context.socket(zmq.ROUTER)
self.port = self.socket.bind_to_random_port(address)While this patch effectively closes the remote attack vector by restricting interface bindings, the underlying deserialization mechanism remains unchanged. The system still relies on unsafe pickle deserialization, representing a security design trade-off that leaves localized exploit channels intact.
An exploit chain targeting this vulnerability requires the attacker to identify the randomized TCP port allocated by AsyncRPCServer. Since the port is assigned dynamically during the initialization of the model server, attackers scanning external networks will find an open TCP port managed by the ZMQ routing framework. The ZMQ framework responds to initial handshakes, confirming the protocol version and port status.
Once the open port is identified, the attacker crafts a malicious binary payload using the standard pickle format. A typical conceptual exploit creates a custom class containing a __reduce__ method. This method returns a tuple containing a callable, such as os.system or subprocess.Popen, and its corresponding arguments, such as an operating system command or shell execution string.
When the LMDeploy call_and_response function processes the incoming payload via pickle.loads(), the interpreter immediately invokes the callable specified in the reduction payload to recreate the object. This invocation occurs in the security context of the parent LMDeploy process, resulting in complete system compromise if the service runs under privileged credentials.
Although the official patch in version 0.10.2 successfully restricts remote exploitation by shifting the interface binding from 0.0.0.0 to 127.0.0.1, several residual risks must be evaluated. Local privilege escalation (LPE) remains a significant concern. If multiple unprivileged users share access to the same machine or container cluster, any local process can scan the loopback interface, discover the port, and execute code within the context of the running LMDeploy worker.
Server-Side Request Forgery (SSRF) also remains a viable bypass vector. If another application running on the same host exposes an SSRF vulnerability that supports custom TCP socket interactions, an attacker can use that application as a pivot to relay malicious pickle payloads to the local ZMQ server. This bypasses the interface restriction because the connection originates from 127.0.0.1.
Furthermore, in containerized orchestration environments like Kubernetes or Docker, shared network namespaces can allow adjacent containers to access the loopback interface of the target pod or container. This layout circumvents the interface-based isolation patch, making network isolation policies outside of the container namespace necessary to protect the deployment.
To resolve the vulnerability completely, administrators must upgrade LMDeploy to version 0.10.2 or higher. The upgrade ensures that the zmq_rpc.py server limits its socket listener to loopback interfaces, reducing the immediate remote attack surface to zero. Upgrades can be performed using standard Python package management tools like pip.
For environments where immediate upgrading is not possible, host-level firewalling must be implemented. Use configuration tools such as iptables or ufw to drop incoming traffic to non-standard TCP ports from external network zones. Alternatively, restrict the server's network namespace explicitly using container network security policies.
Developers and operators should also implement running the LMDeploy daemon under a dedicated, low-privileged system user rather than as the root user. This step restricts the file system write access and system privilege scope available to any command executed via pickle deserialization.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
lmdeploy InternLM | >= 0.9.1, < 0.10.2 | 0.10.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-502 |
| Attack Vector | Network |
| Complexity | Low |
| Privileges Required | None |
| EPSS Score | N/A |
| Exploit Status | Proof-of-Concept |
| CISA KEV Status | Not Listed |
The application deserializes untrusted data without sufficiently verifying that the resulting data will be safe.
CVE-2026-118777 is a prototype pollution vulnerability (CWE-1321) in the Forge JavaScript cryptography and networking library (distributed on npm as node-forge) prior to version 1.4.1. The flaw resides in the forge.http client subsystem, where incoming HTTP cookie names and paths parsed from Set-Cookie response headers are stored directly into standard JavaScript objects without key validation or property isolation. By returning crafted Set-Cookie headers containing keys such as __proto__ or constructor, a malicious HTTP server can inject arbitrary properties into Object.prototype, attach attributes to the global Object constructor, or delete built-in prototype methods to cause an application-wide denial of service.
CVE-2026-118775 (GHSA-4642-5mcj-2rqw) is an integer overflow and validation flaw in the ASN.1 serialization module of Forge (node-forge) prior to version 1.4.1. The asn1.oidToDer() function fails to validate the range of the first two arcs of an Object Identifier (OID) and truncates the packed initial subidentifier to a single 8-bit integer. Applications that perform policy checks on untrusted OID strings before encoding them into Distinguished Encoding Rules (DER) format can be tricked into emitting DER bytes that correspond to a completely different, restricted OID.
CVE-2026-119194 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting versions 0.3.3 through 0.6.4. The flaw resides in the schemaless decoding routine ConstructedPayloadDecoderBase._decodeComponentsSchemaless used across BER, CER, and DER codecs. By sending an ASN.1 container populated with child components whose tag identifiers are separated by multiples of CPython's integer hash modulus (sys.hash_info.modulus), an unauthenticated attacker can force worst-case hash collisions in an unbounded Python set, degrading decoding performance from O(N) to O(N^2) and causing a Denial of Service (DoS).
CVE-2026-119195 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting all versions prior to 0.6.5. When decoding constructed strings or indefinite-length ANY types across BER, CER, and certain DER code paths, the payload decoders repeatedly concatenate incoming fragments onto an immutable bytes object using the += operator. Because Python bytes objects cannot be resized in-place, each fragment iteration allocates a new buffer and copies all previously accumulated bytes, resulting in quadratic O(N^2) time and memory copy complexity. An unauthenticated remote attacker can supply a highly fragmented ASN.1 stream to exhaust CPU cycles and stall application worker threads.
An algorithmic complexity vulnerability (CWE-407) in pyasn1 versions prior to 0.6.5 allows unauthenticated attackers to cause a Denial of Service (DoS) via crafted ASN.1 payloads. The flaw resides in the binary-encoded ASN.1 REAL mantissa deserialization routine within RealPayloadDecoder.valueDecoder. By performing iterative byte-string slicing and repeated bitwise shifts on a growing arbitrary-precision integer, the decoder exhibits quadratic time and memory allocation complexity relative to the mantissa length. Supplying a BER-, CER-, or DER-encoded payload with a large REAL mantissa causes excessive CPU consumption and blocks application worker processes.
A vulnerability classified under CWE-407 (Inefficient Algorithmic Complexity) exists in the pyasn1 library prior to version 0.6.5. When parsing constructed ASN.1 BIT STRING payloads under BER, CER, or schema-driven DER encoding rules, the BitStringPayloadDecoder repeatedly shifts and copies the entire accumulated bit value for each incoming fragment. Processing inputs with a large number of small constructed fragments induces quadratic CPU consumption, allowing unauthenticated remote attackers to cause a denial of service via resource exhaustion during the initial decoding phase.