CVE-2026-54134: OctoPrint File Exfiltration via Upload Parameter Injection
A flaw in OctoPrint's upload pipeline lets an authenticated attacker smuggle internal-only file path fields into any upload request, tricking the server into moving arbitrary host files into the publi

The problem
OctoPrint sits a custom Tornado upload handler in front of Flask. That handler streams incoming files to a temp path on disk, then rewrites the request with internal fields (file.path, file.name, file.content_type, file.size) that tell Flask where the temp file lives.
These fields are reserved for internal use only. The fix for the earlier CVE-2025-48067 stripped them from multipart form bodies, but left two other injection channels open: plain URL query parameters, and multipart parser differentials where Tornado and Werkzeug disagree on how to parse an edge-case boundary or Content-Disposition value, letting Flask see fields that Tornado never noticed.
An attacker with FILE_UPLOAD permission can point file.path at any file the OctoPrint process can read, for example ~/.octoprint/config.yaml, and OctoPrint will move it into the uploads folder where it becomes directly downloadable.
Proof of concept
POST /api/files/local?file.path=%2Fhome%2Fpi%2F.octoprint%2Fconfig.yaml&file.name=config.yaml&file.content_type=application%2Foctet-stream&file.size=0 HTTP/1.1
Host: octopi.local
Authorization: Bearer <API_KEY>
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="select"
false
------Boundary--The Tornado handler only filtered reserved fields from the multipart body it parsed itself. Because query-string parameters bypass the multipart parser entirely, they were forwarded untouched to Flask/Werkzeug, which merged them into request.form and acted on them as if the Tornado handler had set them legitimately.
A second bypass used multipart parser differentials: crafting a boundary or Content-Disposition value that Tornado's parser treats as a harmless non-file field but Werkzeug interprets as one of the reserved field names. Either path let an attacker specify an arbitrary file.path, causing OctoPrint to move that host file into the uploads directory.
The 1.11.8 fix adds an early rejection step that refuses the entire request if any reserved field name appears anywhere in the combined query string or body, before the request ever reaches Flask. CWE-73 (External Control of File Name) and CWE-436 (Interpretation Conflict) both apply.
The fix
Upgrade to OctoPrint 1.11.8 or 2.0.0rc3. Restrict FILE_UPLOAD permission to fully trusted users only. If immediate upgrade is not possible, block external network access to the OctoPrint interface at the network layer.
Reported by Koh Jun Sheng (seankohjs) and Jacopo Tediosi (jacopotediosi).