NVD has published CVE-2026-76850, a CVSS 9.8 CRITICAL, network-exploitable vulnerability in LMDeploy — the inference and serving framework widely used to deploy large language models at scale. The flaw lives in LMDeploy's disaggregated-serving peer-to-peer communication layer and is a textbook unsafe-deserialization bug: incoming peer messages are deserialized with Python's pickle.loads() before any type validation occurs, and the endpoint supplying those bytes is controlled by whoever sends the request.
This is about as bad as it gets for AI infrastructure operators. LMDeploy instances handling disaggregated inference (prefill/decode separation) are typically run on GPU-rich hosts with broad internal network reach — exactly the kind of machine an attacker wants as a first foothold. No authentication, no user interaction, remote code execution with the privileges of the inference server process. If you operate LMDeploy in disaggregated (DistServe) mode, treat this as an emergency patching event and assume the service is reachable by anyone who can touch the port.
Technical Analysis
Affected Component
- Product: LMDeploy (
openai-ecosystem inference serving stack, per the NVD record) - Vulnerable code path:
handle_zmq_recvcoroutine inlmdeploy/pytorch/disagg/conn/engine_conn.py - Triggering endpoints:
POST /distserve/p2p_initializeand related disaggregated-serving initialization routes - CVE / Score: CVE-2026-76850, CVSS 9.8 (CRITICAL), attack vector: NETWORK
How the Vulnerability Works
The disaggregated-serving architecture in LMDeploy splits inference work across multiple engine processes that communicate over ZeroMQ. The vulnerability is a two-step design failure:
-
Attacker-controlled peer endpoint. When a client calls
POST /distserve/p2p_initialize, the server-sidep2p_connectlogic takesremote_engine_endpoint_info.zmq_addressdirectly from the request body and passes it toconnect()on a ZMQ PULL socket. There is no allowlist or validation of that address — the server will happily connect to an endpoint the attacker operates. -
Deserialize-then-validate ordering bug. The
handle_zmq_recvcoroutine reads peer-to-peer cache-free requests usingrecv_pyobj(), which internally callspickle.loads()on the received bytes. Only after deserialization completes does the code perform anisinstancecheck againstDistServeCacheFreeRequest. That ordering is fatal:pickle.loads()on attacker-supplied bytes is arbitrary code execution, full stop. The type check is meaningless — by the time it runs, the attacker's__reduce__payload has already executed.
Attack Chain (Defender's View)
- Attacker identifies an exposed LMDeploy instance with disaggregated serving enabled (HTTP API reachable).
- Attacker stands up a malicious ZMQ endpoint serving a crafted pickle payload (e.g., one that invokes
os.system, spawns a reverse shell, or drops a payload viasubprocess). - Attacker sends
POST /distserve/p2p_initializewithremote_engine_endpoint_info.zmq_addresspointing at the malicious endpoint. - The LMDeploy engine connects its PULL socket to the attacker's endpoint, calls
recv_pyobj(), andpickle.loads()executes the payload in the context of the LMDeploy process. - Attacker now has code execution on the GPU host — typically a high-value Linux server with access to model weights, training data, and internal networks.
Exploitation Requirements and Status
- Authentication: None required.
- Preconditions: Disaggregated (DistServe) serving mode must be enabled and the HTTP API reachable by the attacker. Pure single-node deployments that never initialize p2p connections are at lower risk, but verify — do not assume.
- Exploitation status: At the time of writing, check the NVD entry and CISA KEV for current status. Given the CVSS 9.8 score, the trivial reliability of pickle-based RCE, and the growing attacker interest in AI/ML infrastructure, defenders should operate as if exploitation is imminent even before a public PoC surfaces. Pickle deserialization flaws are among the fastest to weaponize once disclosed — the payload mechanics are public knowledge.
Detection & Response
Detection here focuses on three observable behaviors: (1) the LMDeploy server process making outbound ZMQ/network connections to unusual destinations triggered by API requests, (2) the LMDeploy Python process spawning unexpected child processes (the classic post-pickle-RCE artifact), and (3) suspicious inbound calls to the /distserve/ API routes.
Sigma Rules
---
title: LMDeploy Python Process Spawning Suspicious Child Processes
id: 4f9c2b71-8e3a-4d12-b6f7-2a5c9d1e8f03
status: experimental
description: Detects the LMDeploy inference server Python process spawning shells or command interpreters, consistent with code execution following pickle deserialization exploitation (CVE-2026-76850).
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-76850
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.execution
- attack.t1059.004
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentCommandLine|contains:
- 'lmdeploy'
- 'engine_conn'
- 'distserve'
selection_child:
Image|endswith:
- '/bash'
- '/sh'
- '/dash'
- '/zsh'
- '/curl'
- '/wget'
- '/nc'
- '/ncat'
- '/python'
- '/python3'
- '/perl'
condition: selection_parent and selection_child
falsepositives:
- LMDeploy operational scripts that legitimately invoke subprocesses (rare in serving mode)
level: high
---
title: LMDeploy API Request to Disaggregated Serving p2p Initialization Endpoint
id: 8b2e5d43-1f7a-4c69-9e21-6d3a7b4f0c58
status: experimental
description: Detects HTTP requests to LMDeploy's disaggregated-serving p2p initialization endpoint, the entry point for CVE-2026-76850 exploitation. In environments not using DistServe mode, any such request is suspicious.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-76850
- https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.initial_access
- attack.t1190
logsource:
category: webserver
detection:
selection:
cs-uri-stem|contains:
- '/distserve/p2p_initialize'
- '/distserve/'
condition: selection
falsepositives:
- Legitimate disaggregated-serving cluster initialization traffic
level: medium
---
title: LMDeploy Server Outbound Connection to Non-Cluster Endpoint
id: 2c7a9f16-5d4b-48e3-a193-9f6c2e7d5b41
status: experimental
description: Detects the LMDeploy engine process initiating outbound network connections to external or unrecognized destinations, consistent with p2p_connect being pointed at an attacker-controlled ZMQ endpoint (CVE-2026-76850).
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-76850
- https://attack.mitre.org/techniques/T1071/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.command_and_control
- attack.t1071
logsource:
category: network_connection
product: linux
detection:
selection:
Image|contains: 'python'
CommandLine|contains:
- 'lmdeploy'
- 'distserve'
DestinationIp|cidr:
- '0.0.0.0/0'
filter_internal:
DestinationIp|cidr:
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
condition: selection and not filter_internal
falsepositives:
- Legitimate model downloads or telemetry from the serving host (tune per environment)
level: high
KQL (Microsoft Sentinel / Defender)
The following hunt query targets Syslog/CEF-ingested Linux GPU hosts, looking for the LMDeploy serving process spawning shell children — the highest-fidelity post-exploitation signal:
// Hunt: LMDeploy process spawning suspicious child processes (CVE-2026-76850 post-exploitation)
let lookback = 7d;
union isfuzzy=true
(Syslog
| where TimeGenerated > ago(lookback)
| where ProcessName =~ "bash" or ProcessName =~ "sh" or ProcessName =~ "curl" or ProcessName =~ "wget"
| where SyslogMessage has_any ("lmdeploy", "distserve", "engine_conn")
| project TimeGenerated, Computer, ProcessName, SyslogMessage, HostIP),
(CommonSecurityLog
| where TimeGenerated > ago(lookback)
| where RequestURL has "/distserve/"
| project TimeGenerated, SourceIP, DestinationIP, RequestURL, RequestMethod, DeviceAction)
| order by TimeGenerated desc
For Defender for Endpoint–onboarded Linux servers:
// MDE: Network connections and process trees from LMDeploy python processes
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessCommandLine has_any ("lmdeploy", "distserve")
and (FileName in~ ("bash", "sh", "dash", "curl", "wget", "nc", "python3"))
| project TimeGenerated, DeviceName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, AccountName
| join kind=leftouter (
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessCommandLine has "lmdeploy"
| where not(ipv4_is_private(RemoteIP))
| summarize ExternalConnections=make_set(RemoteIP, 20) by DeviceName, InitiatingProcessId
) on DeviceName, InitiatingProcessId
| order by TimeGenerated desc
Velociraptor VQL
Use this hunt artifact across your GPU/serving fleet to surface LMDeploy processes with unexpected children or unexpected network peers:
-- CVE-2026-76850: Hunt LMDeploy processes with suspicious children or network peers
SELECT Pid, Ppid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ 'lmdeploy|distserve|engine_conn'
OR Exe =~ 'lmdeploy'
-- Companion: map listening/connected sockets for those processes
SELECT Pid, Name, Family, Type, Status,
Laddr AS LocalAddr, Lport AS LocalPort,
Raddr AS RemoteAddr, Rport AS RemotePort
FROM netstat()
WHERE Name =~ 'python'
AND (Rport > 0 OR Status =~ 'LISTEN')
Correlate the two result sets by PID: any LMDeploy-owned Python process holding connections to addresses outside your known cluster topology warrants immediate triage.
Remediation Script
This Bash script inventories LMDeploy deployments on a Linux host, flags whether disaggregated serving is in use, and checks the installed version against the fixed release:
#!/usr/bin/env bash
# CVE-2026-76850 - LMDeploy pickle deserialization RCE: detection & verification helper
set -euo pipefail
echo "=== CVE-2026-76850 LMDeploy Exposure Check ==="
# 1. Locate LMDeploy installations and report versions
for pyenv in $(command -v python3 python pip3 pip 2>/dev/null); do
ver=$("$pyenv" -m pip show lmdeploy 2>/dev/null | awk '/^Version/{print $2}') || true
[ -n "${ver:-}" ] && echo "[FOUND] lmdeploy $ver via $pyenv"
done
find /opt /srv /home /usr/local -maxdepth 6 -type d -name 'lmdeploy' 2>/dev/null | while read -r d; do
echo "[FOUND] lmdeploy source tree: $d"
done
# 2. Check for running LMDeploy / distserve processes
echo "--- Running processes ---"
ps auxww | grep -Ei 'lmdeploy|distserve|engine_conn' | grep -v grep || echo "No LMDeploy processes running."
# 3. Check for exposed serving ports and recent outbound ZMQ-style connections
echo "--- Listening sockets owned by python ---"
ss -tlnp 2>/dev/null | grep -i python || echo "No python listeners found."
echo "--- Established outbound connections from python (potential p2p peers) ---"
ss -tnp 2>/dev/null | grep -i python | grep ESTAB || echo "No established python connections."
# 4. Audit web access logs for distserve API hits (adjust log paths as needed)
echo "--- Recent /distserve/ API requests ---"
for log in /var/log/nginx/access.log /var/log/apache2/access.log /var/log/haproxy.log; do
[ -f "$log" ] && grep -h '/distserve/' "$log" | tail -n 20
done
echo "=== Review findings above. Patch or isolate any exposed instance immediately. ==="
Remediation
- Upgrade LMDeploy immediately to the version containing the fix referenced in the vendor advisory linked from the NVD entry for CVE-2026-76850. Confirm the patched code path:
handle_zmq_recvmust validate/authenticate the peer before anypickle.loads()call, andp2p_connectmust no longer accept arbitraryzmq_addressvalues from request bodies. Verify the fixed version number against the advisory — do not rely on transitive dependency updates. - Until patched, network-isolate the API. Place the LMDeploy HTTP API behind an authenticated gateway or reverse proxy, restrict
/distserve/*routes to trusted cluster IPs only (or block them entirely if you are not using DistServe mode), and enforce mTLS between engines where possible. - Egress control on GPU hosts. Deny outbound connections from inference servers to anything outside an explicit allowlist of cluster peers and model registries. The exploitation chain requires the server to connect out to the attacker's ZMQ endpoint — tight egress policy breaks it even before patching.
- Disable disaggregated serving if unused. If your deployment runs single-node inference, ensure DistServe/p2p mode is not initialized. No p2p socket, no attack surface.
- Hunt before you patch. Run the queries above across a 7–14 day lookback. A pickle RCE on an internet-adjacent GPU host is a high-value intrusion; if you find evidence of
/distserve/requests from unexpected sources, treat it as an incident: isolate the host, capture memory (LMDeploy processes may still hold attacker payloads), and review for lateral movement from the GPU segment. - Adopt a standing rule for AI/ML stacks: any framework that uses
picklefor network messaging should be treated as code-execution-by-design and never exposed beyond a mutually authenticated cluster boundary. Audit your other serving frameworks (Ray, Triton custom backends, vLLM-adjacent tooling) for the same pattern.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.