CyberBytes Daily

Trending cyberattacks, explained simply.

critical vulnerability

How a robotics AI framework's own serialization design left every connected robot open to remote takeover

The developers knew the code was dangerous. Their own static analysis tool flagged it. They responded by adding a comment to silence the warning, and shipped it anyway.

That comment, # nosec, sits directly next to the line of code at the center of CVE-2026-25874: a call to Python's pickle.loads() inside Hugging Face's LeRobot framework, a widely used open-source toolkit for building AI-powered robots. Any attacker who can reach the service port over a network can send a single crafted message and receive full operating system control of the host, no credentials required, no prior access needed. The exploit was confirmed against the official package downloaded directly from PyPI.

The detail that should alarm anyone running AI infrastructure: the same organization that created the safetensors format specifically because pickle is dangerous for machine learning data shipped its own robotics framework using pickle to deserialize commands from the network, with warnings suppressed. Security knowledge and security practice were completely disconnected, inside the same company.

Narrative · 6 min read

The Context

Hugging Face is the dominant platform for sharing and deploying open-source AI models, often described as the GitHub of machine learning. LeRobot is its open-source robotics framework, designed to let researchers and engineers build AI-powered robots using pre-trained policy models. The framework has accumulated between 21,500 and 24,000 GitHub stars, reflecting broad adoption across academic labs, university robotics programs, and production environments.

The specific component at issue is LeRobot's asynchronous inference module, added in September 2025. It offloads computationally expensive AI policy calculations to a dedicated server called the PolicyServer, which communicates with robot clients over gRPC (a high-performance network protocol common within data center or lab networks). The design is sensible for performance. The security implementation was not.

The Attack, Phase by Phase

Phase 1: The Insecure Architecture

When the async inference module shipped in September 2025, no security review was performed on its network design. The PolicyServer was initialized using add_insecure_port(), meaning it operates with no TLS encryption and no authentication. Any host that can reach the port can connect.

The deeper problem is what happens after connection. Both the PolicyServer and its robot clients use Python's pickle.loads() to deserialize all data exchanged over the channel. The design treats every network peer as implicitly trusted—which is only safe if the network itself is perfectly controlled. In practice, it is not.

Developers were aware the design was flagged as dangerous. Their own static analysis linter identified the pickle.loads() calls as security risks. Rather than replacing the unsafe pattern, developers added # nosec comments directly adjacent to the calls, instructing the linter to ignore them. The warnings were silenced. The code shipped.

LEROBOT ASYNC INFERENCE ARCHITECTURE🤖1PolicyServer starts upBinds to port via add_insecure_port()📡2No TLS, no authAny host on the network can connect⚠️3Linter flags pickle.loads()Developers add # nosec to suppress it📦4pickle.loads() ships in productionAll incoming data deserialized unsafelyThe async inference module was added in September 2025 with no security review.

Phase 2: The Vulnerability Window

In December 2025, a researcher using the handle "chenpinji" privately reported the issue to Hugging Face through GitHub's Security tab. No response was received. On January 7, 2026, a LeRobot maintainer publicly acknowledged the risk in a GitHub issue, writing that the codebase "needs to be almost entirely refactored." No fix was issued. The last maintainer interaction on the thread was January 12, 2026.

On February 11, 2026, security researcher Valentin Lobstein independently rediscovered the vulnerability and confirmed it with a working proof-of-concept against the official LeRobot v0.4.3 package installed directly from PyPI. A CVE was reserved on February 6 and published on April 23, 2026 with a critical severity score of 9.3 out of 10. As of late April 2026, no patch exists. A fix is planned for version 0.6.0, with no confirmed release date.

DISCLOSURE TIMELINE🔍1Dec 2025: Private report filedchenpinji reports via GitHub Security tab💬2Jan 7, 2026: Risk acknowledgedMaintainer confirms issue, no fix issued🔇3Jan 12, 2026: Thread goes silentNo further maintainer response🧪4Feb 11, 2026: PoC confirmedLobstein validates against PyPI v0.4.3📢5Apr 23, 2026: CVE publishedCVSS 9.3 Critical, still unpatchedThe vulnerability was known internally for at least four months before public disclosure.

Phase 3: How the Exploit Works

An attacker with network access to the PolicyServer port crafts a malicious Python pickle object. Using Python's __reduce__() hook, they embed an arbitrary operating system command inside the object and send it to any of three vulnerable endpoints on the server.

The critical detail is the order of operations: pickle.loads() is called before any type validation or safety check runs. The embedded command executes the moment the server processes the message—before the server has any opportunity to inspect or reject the payload. No credentials, no prior access, and no complex attack chain are required.

The attack is also bidirectional. The GetActions endpoint, which the server uses to push AI policy decisions back to robot clients, also deserializes data using pickle.loads(). A compromised server can push malicious payloads to every connected robot client, turning a single server compromise into a fleet-wide takeover.

ATTACKER EXPLOIT PATH🎯1Attacker reaches service portNo credentials needed, no TLS📦2Crafts malicious pickle payloadEmbeds OS command via __reduce__()📨3Sends to any of 3 RPC endpointsSendPolicyInstructions, SendObservations, GetActions💥4pickle.loads() fires before validationOS command executes on the serverSERVER BOUNDARY💻Full OS code executionWith process privileges on the host🤖Robot clients also compromisedGetActions path is bidirectionalResecurity confirmed the exploit writes /etc/passwd to /tmp/x on the target host.

Phase 4: Post-Exploitation Impact

AI inference servers typically run with elevated system privileges to manage GPU resources and large datasets. A successful exploit yields full operating system control of that host. From there, an attacker can move laterally to every connected robot client, steal Hugging Face API keys and proprietary model files, corrupt the AI policy models governing robot behavior, and—in deployments where the policy server controls physical hardware—potentially cause unsafe physical operations.

The absence of authentication or encryption on the gRPC channel also means an attacker can maintain persistent access by continuing to send commands through the same exposed port, with no need to install additional malware.

POST-EXPLOITATION BLAST RADIUS🖥️1OS-level control of hostElevated GPU process privileges🔑2API keys and model files stolenHugging Face credentials, proprietary ML data🤖3Robot clients taken overGetActions RPC pushes payloads to fleet⚙️4Physical safety riskCompromised policy controls real hardwarePersistent access requires no additional malware: the open gRPC port remains a standing command channel.

What Made This Possible

  1. Authentication was never built in. The gRPC server was initialized with add_insecure_port() from the start. The entire trust model depended on network isolation. The moment a deployment bound the service to a non-localhost address, that model collapsed.

  2. An unsafe serialization format was used for network data. Python's pickle was designed for local object storage, not for deserializing data arriving from the network. Hugging Face itself created the safetensors format to replace pickle in ML pipelines precisely because of this danger—then shipped pickle over an unauthenticated network channel in its own robotics framework.

  3. Security signals were actively suppressed. The # nosec comments are not an oversight. They represent a deliberate choice to silence a working security tool rather than address what it found. The signal existed. The tooling worked. The decision was made to ignore it.

Research prototyping culture and production deployment culture are not the same thing, and the gap between them is where this vulnerability lived for months.

What Should Have Stopped This

  • Replace pickle with a safe serialization format. The planned fix for v0.6.0 replaces pickle with safetensors for tensor data and JSON for structured messages. Neither format executes code on deserialization. This is the root cause fix.
  • Require authentication on the gRPC channel. Switching from add_insecure_port() to add_secure_port() with mutual TLS means an attacker who reaches the port cannot send commands without a valid credential.
  • Run the service as a non-privileged user inside a container. If the PolicyServer process has no elevated privileges, a successful exploit yields a low-privilege shell rather than full OS control.
  • Treat # nosec comments as a mandatory review trigger. Any suppression of a security linter warning should require documented justification and a second reviewer. A comment that silences a warning without a corresponding fix is a deferred vulnerability, not a resolved one.

The Takeaway

This vulnerability is not primarily a story about a single unsafe function call. It is a story about a research tool adopted in production before its security posture was ever examined—and about an organization that publicly solved this exact class of problem for the broader ML community while leaving it unaddressed in its own codebase.

This is the same class of failure as the Stryker Intune wipe and the Axios supply chain attack: a system trusted by design, where the trust was never validated. The meta-pattern across all three: systems fail when they trust a boundary the attacker controls.

Pattern to remember: When a network service deserializes data before authenticating the sender, the authentication step is irrelevant because the payload executes first.

What changed: A # nosec comment in a codebase is now a meaningful audit signal, not a resolved finding. It proves the warning fired, a human saw it, and the decision was made to ship anyway. Detection without remediation is a documented liability.

Technical Deep Dive · 3 min

The Technical Mechanism

CVE-2026-25874 is classified as CWE-502 (Deserialization of Untrusted Data). The vulnerability exists in LeRobot's async inference pipeline, specifically in policy_server.py and robot_client.py, introduced in September 2025.

The gRPC server is initialized via add_insecure_port(), providing no TLS and no authentication. Protobuf message definitions use raw bytes fields to transport Python objects serialized with pickle. Three RPC handlers are vulnerable:

  • SendPolicyInstructions (unary RPC): The server calls pickle.loads(request.data) on the PolicySetup.data field before any type validation. An attacker sends a crafted PolicySetup message with a malicious pickle payload as data.
  • SendObservations (streaming RPC): The server reassembles chunked Observation.data bytes across the stream and calls pickle.loads() on the reassembled result. The chunking mechanism does not affect exploitability.
  • GetActions (bidirectional RPC): The robot client calls pickle.loads(actions_chunk.data) on Actions.data received from the server. A compromised server can push malicious payloads to all connected clients, making the attack bidirectional.

In all three cases, deserialization occurs before any isinstance() check or other guard. An attacker constructs a malicious class implementing __reduce__() to return a tuple of (os.system, ("command",)) or equivalent. When pickle.loads() processes the payload, Python's deserialization engine calls os.system("command") automatically. The CVSS 4.0 vector AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H confirms: network-accessible, low complexity, no privileges required, no user interaction, full confidentiality/integrity/availability impact.

The # nosec comments adjacent to the pickle.loads() calls confirm that Bandit (Python's standard static analysis security linter) flagged these calls and developers chose suppression over remediation. A separate prior vulnerability, CVE-2025-10772, covers missing authentication in lekiwi_remote.py (ZeroMQ transport), confirming a systemic pattern of authentication gaps across LeRobot's network-facing components.

TECHNICAL EXPLOIT CHAIN📝1Attacker crafts pickle object__reduce__() returns (os.system, cmd)📡2Sends to gRPC bytes fieldAny of 3 RPC endpoints accepts raw bytes3pickle.loads() called firstBefore isinstance() or any validation💻4os.system(cmd) executesWith PolicyServer process privileges🔄5GetActions: server pushes backCompromised server infects robot clientsPoC confirmed: /etc/passwd written to /tmp/x on official LeRobot v0.4.3 from PyPI.

CVE and Advisories

  • CVE-2026-25874: Hugging Face LeRobot unauthenticated RCE via pickle deserialization in gRPC PolicyServer. CVSS 4.0 score: 9.3 (Critical). CWE-502. Reserved February 6, 2026; published April 23, 2026. Assigned by VulnCheck.
  • GHSA-f7vj-73pm-m822: GitHub Security Advisory confirming all three vulnerable RPC paths. No fixed version available; Dependabot alerts not supported due to absence of a patched release in a supported ecosystem.
  • VulnCheck Advisory: Full technical advisory from the CVE assigning organization.
  • GitHub Issue #3047: Public disclosure issue with proposed fix in pull request #3048.

MITRE ATT&CK Mapping

Technique IDATT&CK nameHow it appeared
T1190Exploit Public-Facing ApplicationThe gRPC PolicyServer is a network-accessible service. Attackers exploit the unauthenticated pickle deserialization endpoint to gain initial access without credentials.
T1059.006Command and Scripting Interpreter: PythonThe exploit abuses Python's pickle deserialization engine and __reduce__() hook to execute arbitrary Python and OS-level commands on the target host.
T1552.001Unsecured Credentials: Credentials in FilesPost-exploitation access to the host enables theft of Hugging Face API keys and other credentials stored in environment variables or configuration files on the inference server.
T1565.001Data Manipulation: Stored Data ManipulationAn attacker with OS-level access can corrupt or poison AI policy model files stored on the host, altering robot behavior in subsequent operations.
T1021Remote ServicesThe bidirectional GetActions RPC path enables lateral movement from a compromised PolicyServer to all connected robot clients, which also call pickle.loads() on server responses.

Indicators of Compromise

No network-level indicators of compromise (IOCs) have been published as of April 29, 2026. The attack leaves no distinctive network signature because it uses the legitimate gRPC protocol on the service's standard port with valid protobuf message structure. The malicious content is entirely within the serialized bytes field of the protobuf message, which is opaque to standard network inspection.

Host-level detection is feasible: unexpected child processes spawned by the PolicyServer process, anomalous file writes in /tmp/ or other world-writable directories, and unexpected outbound network connections from the inference server process are all indicators consistent with post-exploitation activity. Reviewing Python process trees for os.system(), subprocess, or shell invocations originating from the gRPC server process is the most direct detection path.

The presence of # nosec comments in policy_server.py adjacent to pickle.loads() calls is a code-level indicator that the unsafe pattern was knowingly retained.

Attribution

No threat actor attribution. This is a vulnerability disclosure. The vulnerability was independently discovered by researcher "chenpinji" (December 2025, private report via GitHub Security tab) and Valentin Lobstein (alias: Chocapikk) of VulnCheck (February 11, 2026, public PoC and CVE assignment). No exploitation in the wild has been reported as of April 29, 2026. Team Cymru threat intelligence advisor Eli Woodward noted that AI infrastructure services of this type are "attractive entry points for both financially motivated actors and more advanced threat groups" due to their privileged access to internal resources.


Primary Sources