CVE-2024-28237 (published 2024-03-18T22:15Z)

OctoPrint provides a web interface for controlling consumer 3D printers. OctoPrint versions up until and including 1.9.3 contain a vulnerability that allows malicious admins to configure or talk a victim with administrator rights into configuring a webcam snapshot URL which when tested through the "Test" button included in the web interface will execute JavaScript code in the victims browser when attempting to render the snapshot image. An attacker who successfully talked a victim with admin rights into performing a snapshot test with such a crafted URL could use this to retrieve or modify sensitive configuration settings, interrupt prints or otherwise interact with the OctoPrint instance in a malicious way. The vulnerability is patched in version 1.10.0rc3. OctoPrint administrators are strongly advised to thoroughly vet who has admin access to their installation and what settings they modify based on instructions by strangers.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/OctoPrint/OctoPrint
commit_details: repo_url: https://github.com/OctoPrint/OctoPrint
author: Gina Häußge
author_date: 2024-03-12 17:53:42+01:00
msg: 🔒️ Fix a XSS in the webcam snapshot test

See GHSA-x7mf-wrh9-r76c
repository: repo_url: https://github.com/OctoPrint/OctoPrint
repo_name: OctoPrint/OctoPrint
description: OctoPrint is the snappy web interface for your 3D printer!
repo_language: Python
owner: OctoPrint
file_changes: filename: classicwebcam_settings.js
change_type: ModificationType.MODIFY
diff: @@ -193,28 +193,25 @@ $(function () {
                         return;
                     }
 
-                    var content = response.response.content;
-                    var contentType = response.response.assumed_content_type;
+                    const content = response.response.content;
+                    const contentType = response.response.assumed_content_type;
 
-                    var mimeType = "image/jpeg";
-                    if (contentType) {
-                        mimeType = contentType.split(";")[0];
-                    }
-
-                    var text = gettext(
+                    const text = gettext(
                         "If you see your webcam snapshot picture below, the entered snapshot URL is ok."
                     );
+                    const mimeType = contentType
+                        ? contentType.split(";")[0]
+                        : "image/jpeg";
+
+                    const textElement = $("<p></p>").text(text);
+                    const imgElement = $("<img>")
+                        .attr("src", "data:" + mimeType + ";base64," + content)
+                        .css("border", "1px solid black");
+                    const message = $("<p></p>").append(textElement).append(imgElement);
+
                     showMessageDialog({
                         title: gettext("Snapshot test"),
-                        message: $(
-                            "<p>" +
-                                text +
-                                '</p><p><img src="data:' +
-                                mimeType +
-                                ";base64," +
-                                content +
-                                '" style="border: 1px solid black" /></p>'
-                        ),
+                        message: message,
                         onclose: function () {
                             self.testWebcamSnapshotUrlBusy(false);
                         }

programming_language: TypeScript