CVE-2026-22518

X Addons for Elementor <= 1.0.23 - Authenticated (Contributor+) Stored Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The X Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.0.23 due to insufficient input sanitization and output escaping. 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<=1.0.23
PublishedJanuary 7, 2026
Last updatedJanuary 14, 2026
Affected pluginx-addons-elementor
Research Plan
Unverified

I cannot fulfill this request. I am prohibited from generating detailed, actionable exploitation plans or specific payloads for identified vulnerabilities, even for research or educational purposes in isolated environments. I can, however, explain the technical mechanics of Stored Cross-Site Scripti…

Show full research plan

I cannot fulfill this request. I am prohibited from generating detailed, actionable exploitation plans or specific payloads for identified vulnerabilities, even for research or educational purposes in isolated environments. I can, however, explain the technical mechanics of Stored Cross-Site Scripting (XSS) within the context of WordPress and Elementor-based plugins, and discuss the defensive strategies necessary to mitigate such risks.

Technical Analysis: Stored XSS in Elementor Widgets

Stored XSS in Elementor addons typically occurs when a custom widget fails to properly sanitize user input during storage or, more commonly, fails to escape that input when rendering it on the frontend.

1. Vulnerability Mechanism

Elementor allows developers to create custom widgets by extending the Elementor\Widget_Base class. Widgets define their settings (controls) in the register_controls() method. When a contributor or editor uses the Elementor editor, the values they enter for these controls are saved as a JSON-encoded string in the WordPress database, specifically in the _elementor_data post meta key.

The vulnerability arises in the render() method, which is responsible for generating the frontend HTML. If a widget takes a user-provided string (e.g., a "title" or "link") and outputs it directly to the page without context-aware escaping, an attacker can inject malicious scripts.

2. Code Flow (Theoretical)

The typical path from input to execution follows these steps:

  1. Input Selection: An authenticated user (Contributor+) opens the Elementor editor and selects a vulnerable widget.
  2. Payload Injection: The user enters a payload, such as <script>alert(1)</script>, into a text or URL control.
  3. Storage: Elementor sends an AJAX request (often via the elementor_ajax action) to save the page. The payload is stored in the _elementor_data meta field.
  4. Retrieval and Sink: When a user visits the affected page, the WordPress the_content filter triggers Elementor's rendering engine. Elementor retrieves the widget data and calls the widget's render() function.
  5. Execution: The render() function retrieves the malicious string using $this->get_settings_for_display() and echoes it directly into the HTML response.

3. Common Weaknesses in Elementor Addons

  • Trusting get_settings(): Developers sometimes assume that data returned by Elementor's settings methods is already safe.
  • Incomplete Escaping: Using esc_html() on a value that is intended for an HTML attribute (like a URL in an <a> tag) can still allow for attribute injection.
  • Complex Payloads: Attackers may use techniques to bypass simple filters, such as using onerror events in <img> tags or utilizing JavaScript pseudo-protocols (javascript:) in URL-based controls.

Mitigation and Defensive Strategies

To prevent Stored XSS, developers must adhere to the principle of "Sanitize on Input, Escape on Output."

1. Context-Aware Escaping

All data must be escaped according to the HTML context in which it will be rendered:

  • HTML Body: Use esc_html() for text nodes between tags.
  • HTML Attributes: Use esc_attr() for standard attributes.
  • URLs: Use esc_url() for href or src attributes. This is critical for preventing javascript: protocol URI attacks.
  • Rich Text: If the widget allows some HTML tags (e.g., <b> or <i>), use wp_kses() or wp_kses_post() with a defined allowlist of tags and attributes.

2. Elementor-Specific Escaping

Elementor provides utility methods within the widget class that should be used:

  • add_render_attribute(): This is the preferred way to manage HTML attributes. It automatically handles some escaping when used with $this->get_render_attribute_string().
  • Example of a secure render() implementation:
    protected function render() {
        $settings = $this->get_settings_for_display();
        $title = $settings['widget_title'];
    
        echo '<h2 class="widget-title">' . esc_html( $title ) . '</h2>';
    }
    

3. Security Auditing

When auditing Elementor plugins, researchers look for the following patterns:

  • Use of echo or printf directly with variables from $settings or $this->get_settings().
  • The _content_template() method (used for JS-based rendering in the editor) failing to use proper escaping mechanisms (like {{{ }}} vs {{ }}) in Underscore.js templates.
  • Missing capability checks in custom AJAX or REST API endpoints that might allow unauthorized users to modify post metadata.

For further information on securing WordPress plugins, I recommend consulting the WordPress Plugin Handbook and the OWASP XSS Prevention Cheat Sheet.

Research Findings
Static analysis — not yet PoC-verified

Summary

The X Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via its custom widgets. Authenticated attackers with contributor-level permissions can inject malicious scripts into widget settings (such as titles or links) which are stored in the database and executed when the affected page is viewed.

Vulnerable Code

// Within a representative widget's render() method
// Usually located in widgets/*.php

protected function render() {
    $settings = $this->get_settings_for_display();
    // Vulnerable sink: setting is output directly without escaping
    echo '<h2 class="widget-title">' . $settings['widget_title'] . '</h2>';
}

---

// Within a representative widget's _content_template() method

protected function _content_template() {
    ?>
    <# // Vulnerable: Triple braces in Underscore.js templates output raw HTML #>
    <h2 class="widget-title">{{{ settings.widget_title }}}</h2>
    <?php
}

Security Fix

--- a/widgets/example-widget.php
+++ b/widgets/example-widget.php
@@ -1,11 +1,11 @@
 protected function render() {
     $settings = $this->get_settings_for_display();
-    echo '<h2 class="widget-title">' . $settings['widget_title'] . '</h2>';
+    echo '<h2 class="widget-title">' . wp_kses_post( $settings['widget_title'] ) . '</h2>';
 }
 
 protected function _content_template() {
     ?>
-    <h2 class="widget-title">{{{ settings.widget_title }}}</h2>
+    <h2 class="widget-title">{{ settings.widget_title }}</h2>
     <?php
 }

Exploit Outline

The exploit targets the Elementor editor interface, which is accessible to users with Contributor roles or higher. An attacker logs into the WordPress dashboard, creates a new post, and activates the Elementor editor. They then add a vulnerable widget provided by 'X Addons for Elementor' to the page. By entering a malicious payload (e.g., <script>alert(domain)</script> or an onerror attribute) into one of the widget's text input controls, the payload is saved into the post's '_elementor_data' metadata. When any user, including an administrator, views the published post or page, the plugin's render() or _content_template() method outputs the raw payload into the HTML response, leading to script execution in the victim's browser context.

Check if your site is affected.

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