Jul 7, 2026·8 min read·1 visit
A DOM-based Stored XSS in Craft CMS (5.0.0-RC1 to 5.9.22) allows low-privileged authors to execute arbitrary JavaScript in admin sessions when admins perform drag-and-drop operations on affected entry structures.
CVE-2026-55793 is a DOM-based Stored Cross-Site Scripting (XSS) vulnerability affecting Craft CMS versions 5.0.0-RC1 through 5.9.22. An authenticated user with minimum Author privileges can store a malicious payload in an entry's title. When an administrator or high-privileged user performs a drag-and-drop operation under the modified entry in the structure table view, the unescaped payload is retrieved and concatenated into raw HTML, resulting in arbitrary JavaScript execution within the context of the administrative session.
Craft CMS incorporates a dynamic structure management interface allowing content editors to establish hierarchical relationships between different entries. This system operates using an interactive drag-and-drop mechanism managed on the client side by the Garnish UI library and Craft's ElementTableSorter.js module. Under normal operations, users with appropriate entry creation permissions can organize entries into complex trees containing multiple levels of depth.
This dynamic hierarchy relies on client-side state tracking to determine when structural elements, such as expand and collapse toggles, need to be created or updated. When an element is modified inside the table view, the front-end application dynamically reconstructs the associated toggle buttons. This action relies on metadata stored in the HTML document to populate accessibility strings, specifically constructing labels to inform screen readers which children are being manipulated.
The attack surface for this vulnerability is exposed through entry creation and modification interfaces. Users with low-privileged access, such as the Author role, can save entries containing custom titles. While the server performs routine HTML escaping before outputting the structural table view, client-side scripts extract and deserialize these values, introducing an indirect boundary transit from untrusted database storage to dynamic execution sinks.
The root cause of CVE-2026-55793 resides in the unsafe programmatic pattern of dynamic HTML string concatenation followed by jQuery evaluation. When Craft CMS serves the entry table view, the server-side rendering engine translates database values into DOM elements, encoding raw characters into HTML entities. For instance, an entry title containing special characters is rendered inside a table row's dataset attribute: <tr data-title=""><script>alert(1)</script>">.
When the browser parses this server-rendered HTML, it automatically decodes the entities during DOM initialization. Consequently, the literal, unescaped payload string resides in the memory space of the DOM object. Inside ElementTableSorter.js, when a drag-and-drop event modifies the position of a row, the script checks if the parent node requires a toggle button. The script queries the parent table row's attributes using the jQuery .data('title') API, which extracts this unescaped string directly from memory.
The script then invokes the localization translation function Craft.t('app', 'Show {title} children', {title: ancestorTitle}). This function interpolates the unescaped title string into the broader UI translation block without performing any context-aware escaping. The translated string is subsequently concatenated directly into a raw HTML template string representing a button element, preserving the raw, unneutralized quotes and bracket characters of the attacker's original payload.
Finally, the concatenated string is passed directly into the jQuery selector function $(). Because the argument starts with a < character, jQuery recognizes it as markup and passes it to internal parsing utilities such as innerHTML or document.createElement. During this translation into active DOM elements, the browser evaluates the attacker-controlled HTML attributes, breaking out of the designated attribute context and executing the nested scripting code.
To understand the vulnerability mechanics, analyze the code before and after the patch inside src/web/assets/cp/src/js/ElementTableSorter.js. The vulnerable implementation used dynamic string concatenation to construct DOM elements:
// Vulnerable implementation prior to version 5.9.23
if (this._updateAncestors._$ancestor.data('descendants') == 1) {
// Create its toggle
const ancestorTitle = this._updateAncestors._$ancestor.data('title');
$(
'<button class="toggle expanded" type="button" aria-expanded="true" title="' +
Craft.t('app', 'Show/hide children') +
'" aria-label="' +
Craft.t('app', 'Show {title} children', {title: ancestorTitle}) +
'"></button>'
).insertAfter(
this._updateAncestors._$ancestor.find('> th .move:first')
);
}In this vulnerable block, the value of ancestorTitle is directly placed within the double quotes of the aria-label attribute inside the raw HTML string. If an attacker passes a value containing a double-quote character, it prematurely terminates the attribute definition and introduces secondary attributes or complete HTML tags into the parser.
To address this issue, the patch modifies how the button is built, switching to a structured jQuery element declaration signature:
// Patched implementation in version 5.9.23
if (this._updateAncestors._$ancestor.data('descendants') == 1) {
// Create its toggle
const ancestorTitle = this._updateAncestors._$ancestor.data('title');
$('<button/>', {
class: 'toggle expanded',
type: 'button',
'aria-expanded': 'true',
title: Craft.t('app', 'Show/hide children'),
'aria-label': Craft.t('app', 'Show {title} children', {
title: ancestorTitle,
}),
}).insertAfter(
this._updateAncestors._$ancestor.find('> th .move:first')
);
}This revised construction pattern passes the element tag name as the first argument, and all properties and attributes inside an object configuration as the second argument. When jQuery processes this configuration, it instantiates an empty button element and updates the properties programmatically using native DOM APIs such as Element.setAttribute() and Node.textContent. This treats all dictionary values strictly as literal string parameters, preventing any embedded HTML character sequences from compiling into executable instructions.
This remediation is highly complete because it eliminates the possibility of string breakout. Even if the translation helper interpolates a malicious payload, the browser interprets the resulting text strictly as a flat string value within the aria-label attribute of the element, rendering the XSS attack vector obsolete.
Exploitation of CVE-2026-55793 requires a multi-stage workflow involving configuration manipulation, specific payload design, and targeted user interaction. An attacker must first authenticate with an account holding sufficient permissions to write or update entry titles within a designated Structure section.
The attacker crafts a payload designed to break out of the button's attribute wrapper and register an active event handler. Because the vulnerable code wraps the translation within double quotes, the payload begins with a double quote to close the aria-label parameter, followed by an execution trigger. A standard, highly reliable proof-of-concept payload utilizes the autofocus and onfocus parameters:
x" autofocus onfocus="fetch('/admin/actions/users/save-user',{method:'POST',body:new URLSearchParams({'userId':1,'admin':1})})" x="Once the payload is saved as the title of an entry, the attacker positions this entry within the hierarchical table structure view. The exploit will not execute automatically when users simply browse or view the entry list. Instead, the attacker must wait for an administrator or another user with elevated privileges (specifically, holding saveEntries capabilities) to perform a structural reorganization.
When the victim drags an unrelated entry row and places it directly underneath the attacker's poisoned entry, ElementTableSorter.js evaluates the update. Because the poisoned entry transitions to having a child, the script extracts the unescaped payload via .data('title'), concatenates it into the HTML string, and dynamically instantiates the button element inside the DOM. The browser immediately processes the autofocus attribute, focusing the newly rendered button and running the arbitrary JavaScript payload within the security context of the victim's administrative session.
The impact of a successful DOM-based Stored XSS vulnerability in a content management platform is severe, as the payload executes with the privileges of the active session. Because the target of this vulnerability must be a user capable of reorganizing and saving structural configurations, the victim is almost always an administrative user or a high-level content manager.
Using the administrative context, an attacker can perform silent, background administrative tasks. This includes executing requests to register a new admin account, changing existing user credentials, altering system configuration settings, or downloading database backups. Because Craft CMS allows administrators to install plugins and edit templates, the attacker can leverage the compromised session to upload custom templates containing PHP web shells, effectively elevating the DOM-based XSS into an unauthenticated Remote Code Execution (RCE) vulnerability on the underlying web server.
Despite this high integrity compromise, the CVSS v4.0 score is designated as 5.9 (Medium) primarily due to operational limitations. The vulnerability cannot be triggered via simple automated crawler sweeps or passive page visits. It requires an authenticated actor to plant the payload and active, physical administrative interactions to trigger the execution flow, reducing the probability of widespread worm-like propagation.
The definitive remediation for CVE-2026-55793 is to upgrade the Craft CMS application suite to version 5.9.23 or higher. This update replaces the vulnerable dynamic HTML generation pattern with structured jQuery element construction in the ElementTableSorter.js component, neutralizing any attempts at markup injection.
If immediate patching is not possible, organizations should deploy temporary administrative mitigations to reduce the vulnerability footprint. This includes auditing user permissions to ensure that only highly trusted users hold the capability to create or edit entries inside Structure sections. Furthermore, access to table views with hierarchical drag-and-drop structures should be monitored, and standard users should have their sorting permissions revoked where feasible.
In addition, security operations teams should deploy Web Application Firewall (WAF) signatures to intercept and analyze payload submission patterns. Requests hitting the /admin/actions/entries/save-entry routing handler should be inspected for characters typical of attribute breakout patterns, such as matching pairs of double quotes followed by focus, load, or error event handlers.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
cms craftcms | >= 5.0.0-RC1, <= 5.9.22 | 5.9.23 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Base Score | 5.9 (Medium) - CVSS v4.0 |
| EPSS Score | 0.00412 |
| Impact | High Integrity Compromise (Session Takeover / Privilege Escalation) |
| Exploit Status | Proof-of-Concept / Technical Analysis Available |
| CISA KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
An access control deficiency in the 9Router dashboard allows unauthenticated remote attackers to perform full CRUD operations on integrated AI providers, extract plaintext API keys, and access complete system conversation histories.
A DOM-based cross-site scripting (XSS) vulnerability exists in Craft CMS versions 4.0.0-RC1 through 4.17.15 and 5.0.0-RC1 through 5.9.22. The flaw resides within the CraftSupport widget's feedback search component, which fails to neutralize GitHub issue titles before rendering them into the administrator's control panel. An unauthenticated attacker can exploit this vulnerability by submitting a crafted issue to the public Craft CMS repository on GitHub.
Craft CMS versions 5.9.0 through 5.9.9 are vulnerable to authenticated Remote Code Execution (RCE). An attacker with control panel permissions to edit entries can inject malicious Twig templates into the client-side HTTP Referer header. During the post-save redirect sequence, the server evaluates this user-controlled header using an unsandboxed Twig rendering function, leading to arbitrary system command execution.
An authenticated SQL injection vulnerability exists in the datapoint crosstab export functionality of OpenRemote. The vulnerability is caused by insecure manual SQL string construction that concatenates user-controlled display data, specifically asset display names and attribute names, directly into raw SQL statements. These statements are processed by the PostgreSQL database engine using the crosstab function to structure dynamic CSV outputs.
An insecure redirect vulnerability in Coder allows an authenticated attacker who controls a workspace agent to perform unauthorized cross-agent file operations and achieve remote code execution in other workspaces. By exploiting default redirect-following behavior in the control-plane's HTTP client, a malicious agent can redirect legitimate requests to a victim's deterministic tailnet IP address.
CVE-2026-35341 is a high-severity vulnerability in the mkfifo utility of uutils coreutils, involving a logic-flow bypass and a TOCTOU race condition that permits unauthorized file permission degradation and privilege escalation.