{"id":"PYSEC-2026-3916","summary":"reachy_mini Allows Unrestricted Upload of File with Dangerous Type","details":"## Summary\n\nThe Reachy Mini daemon exposes the “/api/media/sounds/upload” endpoint without authentication and file validation mechanisms.  \nAn attacker can use this endpoint to upload malicious files into the file system that will propagate in future attacks.\n\n## Compromise Chain: Unauthenticated to Full Root Access\n\nThis issue is part of a full compromise chain allowing an unauthenticated user to gain root access on the Reachy’s operating system:\n\n1. Unrestricted File Upload in Media Sounds Upload API \\\u003c= current finding  \n2. Bluetooth Authentication Bypass  \n3. Bluetooth Directory Traversal\n\n\n## Description\n\nThe root cause of the issue is at the handler located in “***src/daemon/app/routers/media.py***” file at the “upload\\_sound” method:\n\n```py\n@router.post(\"/sounds/upload\")\nasync def upload_sound(\n    file: UploadFile = File(...),\n) -\u003e dict[str, str]:\n    \"\"\"Upload a sound file to the daemon's temporary sound directory.\n    The file is saved to ``/tmp/reachy_mini_sounds/\u003coriginal_filename\u003e``.\n    If a file with the same name already exists it is overwritten.\n    Returns:\n        JSON with the absolute *path* of the saved file on the daemon.\n    \"\"\"\n    if not file.filename:\n        raise HTTPException(status_code=400, detail=\"Filename is required\")\n    # Reject path traversal\n    filename = Path(file.filename).name\n    if not filename or filename in (\".\", \"..\"):\n        raise HTTPException(status_code=400, detail=\"Invalid filename\")\n    os.makedirs(SOUNDS_TMP_DIR, exist_ok=True)\n    dest = os.path.join(SOUNDS_TMP_DIR, filename)\n    content = await file.read()\n    with open(dest, \"wb\") as f:\n        f.write(content)\n    return {\"status\": \"ok\", \"path\": dest}\n```\n\nThis endpoint lacks multiple defence mechanisms:\n\n1. No authentication mechanism.  \n2. No file extension validation.  \n3. No file content/size validation.\n\nAdditionally, the daemon is bound to the 0.0.0.0 network interfaces (a.k.a. all network interfaces) by default along with permissive CORS ( allow\\_origins=\\[“\\*”\\] ) meaning the following API endpoint is exposed to every network interface the daemon is connected to.\n\n# PoC\n\n1. Start the daemon in simulation mode (command depends on the installed environment):\n\n```shell\n .venv/bin/mjpython -m reachy_mini.daemon.app.main --sim --no-media\n```\n\n2. After that check that the media upload API endpoint is activated and you can upload a wav file:\n\n```shell\ncurl -X POST http://\u003cdaemon_domain\u003e:\u003cdaemon_port\u003e/api/media/sounds/upload \\\n    -F \"file=@/path/to/your/file.wav\"\n```\n\n3. Now attempt to create a “.sh” file containing a script and upload it:\n\n```shell\ncurl -X POST http://\u003cdaemon_domain\u003e:\u003cdaemon_port\u003e/api/media/sounds/upload \\\n    -F \"file=@/path/to/your/script.sh\"\n```\n\n4. Now error message will be received and you will see that the script file was successfully uploaded to disk.\n\n# Impact\n\nDue to this issue, an attacker can upload malicious files instead of the intended sounds files, harming the integrity of the stored data and allowing an attacker to propagate a foothold in cases another vulnerabilities would arise.\n\n## Fix suggestion\n\nPerform the following check on the API endpoint:\n\n1. Validate that the file extension contains only desired extensions (allow-list approach).  \n2. Validate that the uploaded file’s content matches the desired extension (Magic numbers, and known file structure per file type).  \n3. Enforce authentication on the file upload endpoint.\n\n## Credit\n\nThe vulnerability was discovered by Natan Nehorai of the JFrog Vulnerability Research team.","aliases":["CVE-2026-55419","GHSA-m2pc-3q4q-w6jr"],"modified":"2026-09-10T12:15:11.905544573Z","published":"2026-09-10T09:44:56.500259Z","references":[{"type":"WEB","url":"https://github.com/pollen-robotics/reachy_mini/security/advisories/GHSA-m2pc-3q4q-w6jr"},{"type":"WEB","url":"https://github.com/pollen-robotics/reachy_mini/commit/984c7723b3ec5da63f4e0a2bcf9f120ceb563e04"},{"type":"PACKAGE","url":"https://github.com/pollen-robotics/reachy_mini"},{"type":"PACKAGE","url":"https://pypi.org/project/reachy-mini"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-m2pc-3q4q-w6jr"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-55419"}],"affected":[{"package":{"name":"reachy-mini","ecosystem":"PyPI","purl":"pkg:pypi/reachy-mini"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.8.2"}]}],"versions":["1.0.0","1.0.0rc1","1.0.0rc4","1.0.0rc5","1.1.0","1.1.0rc3","1.1.0rc4","1.1.1","1.1.2","1.1.3","1.1.4","1.2.0","1.2.0rc1","1.2.1","1.2.10","1.2.11","1.2.13","1.2.2","1.2.3","1.2.3rc1","1.2.4","1.2.4rc1","1.2.4rc2","1.2.4rc3","1.2.4rc4","1.2.5","1.2.5rc1","1.2.5rc2","1.2.5rc3","1.2.5rc4","1.2.6","1.2.6rc1","1.2.6rc2","1.2.6rc3","1.2.7","1.2.7rc1","1.2.7rc2","1.2.8","1.2.9","1.3.0","1.3.1","1.4.0","1.4.0.dev0","1.4.1","1.5.0","1.5.1","1.6.0","1.6.1","1.6.2","1.6.3","1.6.4","1.7.0","1.7.1","1.7.2","1.7.3","1.8.0","1.8.0rc1","1.8.1"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/reachy-mini/PYSEC-2026-3916.yaml"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N"}]}