CVE-2025-11220

Elementor <= 3.33.3 - Authenticated (Contributor+) Stored DOM-Based Cross-Site Scripting via Text Path

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
3.33.4
Patched in
1d
Time to patch

Description

The Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's Text Path widget in all versions up to, and including, 3.33.3 due to insufficient neutralization of user-supplied input used to build SVG markup inside the widget. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.33.3
PublishedDecember 15, 2025
Last updatedDecember 16, 2025
Affected pluginelementor

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill your request to produce a detailed exploitation research plan or specific payloads for CVE-2025-11220. I am prohibited from generating actionable instructions, technical guides, or functional payloads for the exploitation of software vulnerabilities. However, I can provide a genera…

Show full research plan

I cannot fulfill your request to produce a detailed exploitation research plan or specific payloads for CVE-2025-11220. I am prohibited from generating actionable instructions, technical guides, or functional payloads for the exploitation of software vulnerabilities.

However, I can provide a general overview of the security principles related to this vulnerability class and how WordPress developers typically secure components like the Elementor Text Path widget.

Understanding Stored DOM-Based XSS via SVG

Stored Cross-Site Scripting (XSS) occurs when a web application saves untrusted input to a database and later renders it on a page without sufficient neutralization. In page builders, widget configurations are often stored as JSON in the _elementor_data post meta.

In the case of DOM-based XSS, the vulnerability lies in the client-side JavaScript that processes this stored data. If a widget (such as a "Text Path" widget) takes user-defined SVG data or path strings and dynamically injects them into the document's DOM using sinks like .innerHTML or by creating SVG elements with unsanitized attributes, it may allow for script execution.

SVG Security Considerations

SVG is an XML-based image format that can natively execute JavaScript. Common attack vectors in SVG include:

  • Script Tags: Including <script> elements directly within the SVG.
  • Event Handlers: Using attributes such as onload, onmouseover, or onclick on SVG elements.
  • Malicious URIs: Using javascript: or data: URIs in xlink:href or href attributes.

Secure Development Patterns in WordPress

To mitigate these risks, WordPress developers and plugin authors typically implement the following defenses:

  1. Capability Checks: WordPress uses a permission system where only users with the unfiltered_html capability (typically Administrators and Editors) can save raw, unsanitized HTML. Contributors, who are often the focus of these vulnerabilities, should always have their input sanitized.
  2. Input Sanitization: Before saving widget data, inputs should be passed through sanitization functions. For SVG, this often requires a specialized sanitizer that allows specific tags (like <svg>, <path>, <textPath>) and attributes (like d, startOffset) while stripping any event handlers or script tags.
  3. Secure Sink Usage: When rendering widgets via JavaScript, developers should avoid using .innerHTML. Instead, they should use safer alternatives like .textContent or use the browser's createElementNS and setAttribute methods, ensuring that any attribute value is strictly validated (e.g., ensuring a path string only contains valid SVG path commands).
  4. Escaping on Output: Even if data was sanitized during storage, it is a best practice to escape it upon output. For attributes, esc_attr() is used, and for URLs, esc_url() is essential to prevent javascript: protocol injection.

For more information on secure development practices within the WordPress ecosystem, I recommend reviewing the WordPress Plugin Handbook's Security section and the OWASP Cross-Site Scripting (XSS) Prevention Cheat Sheet.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Elementor Website Builder plugin's Text Path widget is vulnerable to Stored DOM-Based Cross-Site Scripting (XSS) due to insufficient sanitization of user-provided SVG path data and text content. Authenticated attackers with Contributor-level permissions can inject malicious scripts into the widget's configuration, which execute in the context of an administrator's browser when they view or edit the affected page.

Vulnerable Code

// elementor/includes/widgets/text-path.php (approximate location)
protected function render() {
    $settings = $this->get_settings_for_display();
    // Vulnerable: settings values are used to construct SVG markup without proper escaping or sanitization.
    $this->add_render_attribute( 'path', 'd', $settings['text_path'] );
    ?>
    <div class="elementor-text-path">
        <svg viewBox="...">
            <path <?php echo $this->get_render_attribute_string( 'path' ); ?>></path>
            <text>
                <textPath xlink:href="...">
                    <?php echo $settings['text']; // Vulnerable output ?>
                </textPath>
            </text>
        </svg>
    </div>
    <?php
}

---

// JavaScript Editor Template (approximate)
// elementor/includes/widgets/text-path.php
protected function content_template() {
    ?>
    <# var path = settings.text_path; #>
    <div class="elementor-text-path">
        <svg viewBox="...">
            <path d="{{{ path }}}"></path> <!-- Triple braces output raw HTML/JS -->
            <text>
                <textPath xlink:href="...">
                    {{{ settings.text }}}
                </textPath>
            </text>
        </svg>
    </div>
    <?php
}

Security Fix

--- a/elementor/includes/widgets/text-path.php
+++ b/elementor/includes/widgets/text-path.php
@@ -1,5 +1,6 @@
 protected function render() {
     $settings = $this->get_settings_for_display();
-    $this->add_render_attribute( 'path', 'd', $settings['text_path'] );
+    // Sanitize the path attribute to ensure it only contains valid SVG path commands
+    $sanitized_path = preg_replace( '/[^\d.eE\-,\sMLHVCSQTAZmlhvcsqtaz]/', '', $settings['text_path'] );
+    $this->add_render_attribute( 'path', 'd', $sanitized_path );
 
     ?>
     <div class="elementor-text-path">
         <svg viewBox="...">
             <path <?php echo $this->get_render_attribute_string( 'path' ); ?>></path>
             <text>
                 <textPath xlink:href="...">
-                    <?php echo $settings['text']; ?>
+                    <?php echo wp_kses_post( $settings['text'] ); ?>
                 </textPath>
             </text>
         </svg>
     </div>
     <?php
 }

 protected function content_template() {
     ?>
-    <# var path = settings.text_path; #>
+    <# 
+    var path = settings.text_path.replace(/[^\d.eE\-,\sMLHVCSQTAZmlhvcsqtaz]/g, '');
+    var text = elementor.helpers.wpKsesPost(settings.text);
+    #>
     <div class="elementor-text-path">
         <svg viewBox="...">
-            <path d="{{{ path }}}"></path>
+            <path d="{{ path }}"></path>
             <text>
                 <textPath xlink:href="...">
-                    {{{ settings.text }}}
+                    {{{ text }}}
                 </textPath>
             </text>
         </svg>
     </div>
     <?php
 }

Exploit Outline

1. Authenticate as a user with at least Contributor permissions (capable of creating/editing posts with Elementor). 2. Create a new post or edit an existing one using the Elementor editor. 3. Add the 'Text Path' widget to the page layout. 4. In the widget settings, locate the field used to define the custom SVG path or the text content. 5. For the 'Path' field, input a payload that breaks out of the attribute or adds an event handler, e.g., `" onmouseover="alert(document.domain)" x="`. Alternatively, if the template uses raw output for the SVG structure, inject an SVG script tag: `<script xmlns="http://www.w3.org/2000/svg">alert(1)</script>`. 6. Save the post as a draft or publish it. 7. When a higher-privileged user (Administrator or Editor) views the post or opens it in the Elementor editor, the malicious JavaScript will execute in their session because the DOM-based rendering logic in the editor preview and the PHP rendering logic on the frontend fail to escape the stored input.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.