Detect LMDeploy Pickle Deserialization RCE via zmq_rpc.call_and_response() (CVE-2025-59953) in Microsoft Sentinel
Detects exploitation of CVE-2025-59953, a critical (CVSS 9.8) unsafe Python pickle deserialization remote code execution vulnerability in InternLM's LMDeploy. The ZeroMQ RPC layer (zmq_rpc.call_and_response()) deserializes attacker-controlled messages with Python's pickle module, allowing an unauthenticated attacker who can reach the internal ZMQ RPC sockets to execute arbitrary code via a malicious pickle payload (CWE-502). Affected versions are >= 0.9.1 and < 0.10.2; fixed in 0.10.2. This detection surfaces suspicious child processes spawned by the LMDeploy Python service, anomalous inbound connections to LMDeploy ZMQ RPC ports, and pickle __reduce__ gadget indicators in traffic/logs.
MITRE ATT&CK
- Tactic
- Initial Access Execution
KQL Detection Query
let lmdeployParents = dynamic(["python","python3","lmdeploy","uvicorn","gunicorn"]);
let suspiciousChildren = dynamic(["sh","bash","dash","zsh","nc","ncat","netcat","curl","wget","powershell.exe","cmd.exe","whoami","id","perl","ruby"]);
DeviceProcessEvents
| where InitiatingProcessFileName has_any (lmdeployParents)
and (InitiatingProcessCommandLine has_any ("lmdeploy","zmq_rpc","turbomind","pytorch_engine","api_server") )
| where FileName has_any (suspiciousChildren)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName, InitiatingProcessId, ProcessId
| order by Timestamp desc Flags shell/recon/network child processes spawned by an LMDeploy Python service, the primary post-exploitation signal of the pickle RCE.
Data Sources
Required Tables
False Positives & Tuning
- Legitimate LMDeploy startup scripts that shell out to nvidia-smi or environment-setup wrappers
- Administrators or CI pipelines running diagnostic commands inside the model-serving container
- Model deployment automation invoking curl/wget to fetch weights or health-check endpoints
Other platforms for CVE-2025-59953
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Simulate LMDeploy pickle RCE child shell spawn
Expected signal: Process-creation event with parent image lmdeploy_sim (python) spawning /bin/sh executing id/whoami
- Test 2Malicious pickle __reduce__ gadget execution (lab)
Expected signal: python3 process invoking os.system which spawns sh -c touch, plus creation of /tmp/cve_2025_59953_poc
- Test 3Inbound connection to LMDeploy ZMQ RPC port
Expected signal: Network connection event to TCP/5555 carrying a pickle protocol marker (\x80) payload
Response Playbook
Triage
- Confirm the LMDeploy version on the affected host (pip show lmdeploy); any version >= 0.9.1 and < 0.10.2 is vulnerable and should be treated as exploitable.
- Identify the parent LMDeploy/Python process and inspect its command line for api_server/turbomind/zmq_rpc arguments and the listening ZMQ RPC ports.
- Review the suspicious child process command line, user context, and working directory to determine whether execution is malicious or benign automation.
- Correlate inbound network connections to the LMDeploy ZMQ RPC sockets around the process-spawn timestamp to identify the source host.
Containment
- Isolate the affected host/container from the network to stop further RPC-delivered pickle payloads.
- Restrict access to LMDeploy ZMQ RPC ports via firewall/security-group rules so only trusted internal hosts can reach them, and stop the exposed api_server if not needed.
- Upgrade LMDeploy to 0.10.2 or later on all affected systems before returning them to service.
Evidence Collection
- Capture the full process tree, command lines, and environment of the LMDeploy service and any spawned children.
- Preserve ZMQ RPC endpoint logs and any packet captures of traffic to the RPC ports, which may contain the pickle payload.
- Collect the LMDeploy application logs, container image digest, and pip freeze output for version confirmation.
Escalation Criteria
- !Escalate to incident response if any child process performed reconnaissance, credential access, lateral movement, or outbound C2 connections.
- !Escalate if the LMDeploy RPC port was reachable from untrusted networks (internet-exposed) or if payload delivery from an external source is confirmed.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Process-creation records showing python/lmdeploy spawning shell or network utilities - >
ZMQ RPC socket logs and packet captures containing pickle opcodes (e.g. leading \x80 protocol markers and 'c__builtin__' / posix system references) - >
LMDeploy application and container logs around the spawn timestamp - >
pip metadata (lmdeploy version) confirming exposure window
Tuning Guidance
Baseline the legitimate child processes of your LMDeploy deployment (e.g. nvidia-smi, environment wrappers, health-check curl) and add them to an allowlist keyed on parent command line plus child image plus arguments. Focus alerting on interactive shells, netcat, and reverse-shell patterns. If LMDeploy runs in a locked-down container that never legitimately spawns shells, tighten to alert on ANY unexpected child process. Prioritize hosts where the ZMQ RPC port is reachable beyond localhost.
Hunting Queries
Hunts for LMDeploy service processes spawning shell/network child processes across the fleet.
DeviceProcessEvents | where InitiatingProcessCommandLine has_any ("lmdeploy","zmq_rpc","api_server") | where FileName in~ ("sh","bash","nc","curl","wget") | summarize count() by DeviceName, InitiatingProcessCommandLine, FileName, bin(Timestamp,1h) index=edr (parent_process=*lmdeploy* OR parent_process=*zmq_rpc*) process_name IN ("sh","bash","nc","curl","wget") | stats count by host parent_process process_name Atomic Red Team Tests
Emulates the post-exploitation signal by having a process named like the LMDeploy service spawn a shell command, matching the detection logic without exploiting a real instance.
Command
cp /usr/bin/python3 /tmp/lmdeploy_sim 2>/dev/null; /tmp/lmdeploy_sim -c "import subprocess; subprocess.call(['/bin/sh','-c','id; whoami'])" Cleanup
rm -f /tmp/lmdeploy_sim Expected Telemetry
Process-creation event with parent image lmdeploy_sim (python) spawning /bin/sh executing id/whoami
Expected Detection
KQL/SPL/EQL rules fire on the python-parent -> sh child correlation
Deserializes a locally crafted malicious pickle in an isolated lab to reproduce the code-execution primitive behind CVE-2025-59953.
Command
python3 -c "import pickle,os,base64; payload=pickle.dumps(type('e',(object,),{'__reduce__':lambda s:(os.system,('touch /tmp/cve_2025_59953_poc',))})()); pickle.loads(payload)" Cleanup
rm -f /tmp/cve_2025_59953_poc Expected Telemetry
python3 process invoking os.system which spawns sh -c touch, plus creation of /tmp/cve_2025_59953_poc
Expected Detection
Endpoint rules flag the python-to-shell child process chain and file-creation artifact
Generates an anomalous inbound TCP connection to a simulated LMDeploy ZMQ RPC listener to validate network-side detection and triage correlation.
Command
python3 -c "import socket,threading,time; srv=socket.socket(); srv.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1); srv.bind(('0.0.0.0',5555)); srv.listen(1); threading.Thread(target=lambda:(time.sleep(1), socket.create_connection(('127.0.0.1',5555)).sendall(b'\x80\x04pickle_payload'))).start(); c,_=srv.accept(); print(c.recv(64)); srv.close()" Cleanup
pkill -f 'bind..0.0.0.0.,5555' 2>/dev/null; true Expected Telemetry
Network connection event to TCP/5555 carrying a pickle protocol marker (\x80) payload
Expected Detection
Network/triage correlation flags inbound traffic to the LMDeploy RPC port around a process-spawn event