CVE-2026-52698

PushEngage – Web Push Notifications, WooCommerce Automation & Chat Widget <= 4.2.3 - Authenticated (Subscriber+) Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
4.2.4
Patched in
9d
Time to patch

Description

The PushEngage – Web Push Notifications, WooCommerce Automation & Chat Widget plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 4.2.3. This makes it possible for authenticated attackers, with Subscriber-level access and above, to extract sensitive user or configuration data.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=4.2.3
PublishedJune 10, 2026
Last updatedJune 18, 2026
Affected pluginpushengage

What Changed in the Fix

Changes introduced in v4.2.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

true` is set, the Abilities API might bypass the `permission_callback` for MCP-enabled clients (which might include a Subscriber-level user role). * `pushengage/get-plugin-info` * `pushengage/get-analytics` * `pushengage/get-segments` * `pushengage/get-audience-groups` *…

Show full research plan

trueis set, the Abilities API might bypass thepermission_callback` for MCP-enabled clients (which might include a Subscriber-level user role).

*   `pushengage/get-plugin-info`
*   `pushengage/get-analytics`
*   `pushengage/get-segments`
*   `pushengage/get-audience-groups`
*   `pushengage/get-auto-push-settings`

*   I'll try `pushengage/get-plugin-info` first.

*   *Pre-conditions*:
    1.  WordPress Abilities API (feature plugin) must be active (or we simulate the environment where PushEngage registers these).
    2.  PushEngage must be configured with an API key (to have a `user` envelope to leak).
    3.  Subscriber user must be created.

*   *Steps*:
    1.  Login as Subscriber.
    2.  Fetch `/wp-json/wp-abilities/v1/call/pushengage/get-plugin-info` via POST.
    3.  Check if response contains the `user` object.

*   *Nonce*: Use `X-WP-Nonce` header with a nonce for the `wp_rest` action.
*   *How to get the nonce?*
    Log in as Subscriber, navigate to `/wp-admin/`, extract `wpApiSettings
Research Findings
Static analysis — not yet PoC-verified

Summary

The PushEngage plugin registers multiple 'abilities' via the WordPress Abilities API and admin-facing dashboard components that fail to properly enforce high-level permissions. This allows authenticated users with Subscriber-level roles to access sensitive account metadata, including owner emails, plan details, and billing-related IDs (like Stripe IDs) by querying REST API endpoints or viewing admin widgets.

Vulnerable Code

// app/DashboardWidget.php:41
public function dashboard_widget() {
	// Get options from settings
	$pushengage_settings = Options::get_site_settings();
	$hide_widget = $pushengage_settings['misc']['hideDashboardWidget'];
	// Return if widget is disabled.
	if ( $hide_widget ) {
		return;
	}
	wp_add_dashboard_widget(
		'pe-dashboard-widget',
		esc_html__( 'PushEngage', 'pushengage' ),
		array( $this, 'dashboard_widget_callback' ),
		null,
		null,
		'normal',
		'high'
	);

---

// app/WhatsNew.php:52
public static function is_showing() {
	global $pagenow;
	$pushengage_settings = Options::get_site_settings();
	$disabled  = ArrayHelper::get( $pushengage_settings, 'dismissed_whats_new_notice', false );

	return 'plugins.php' === $pagenow && ! $disabled;
}

---

// app/Integrations/Abilities/AbstractRegistrar.php:115
protected static function unwrap_envelope( $response, $shape = 'object' ) {
	$data = ( is_array( $response ) && isset( $response['data'] ) && is_array( $response['data'] ) )
		? $response['data']
		: array();

	if ( 'rows' !== $shape ) {
		return $data;
	}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.3/app/Core.php /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.4/app/Core.php
--- /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.3/app/Core.php	2026-05-15 10:46:50.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.4/app/Core.php	2026-05-19 12:57:28.000000000 +0000
@@ -266,14 +266,13 @@
 	* @return void
 	*/
 	public function init_admin_options() {
+		if ( ! current_user_can( 'manage_options' ) || false === $this->is_pushengage_active() ) {
+			return;
+		}
 
 		// Add Meta box when PushEngage site is not connected.
 		add_action( 'add_meta_boxes', array( $this, 'add_pushengage_site_not_connected_metabox' ) );
 
-		if ( ! is_user_logged_in() || false === $this->is_pushengage_active() ) {
-			return;
-		}
-
 		add_action( 'add_meta_boxes', array( $this, 'add_pushengage_settings_metabox' ) );
 
 		// action hook, to handle the case of draft and schedule post to send notification.
@@ -747,6 +746,9 @@
 	 * @return void
 	 */
 	public function load_block_editor_scripts() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			return;
+		}
 		$screen = get_current_screen();
 		if ( ! $screen || ! in_array( $screen->post_type, Options::get_allowed_post_types_for_auto_push(), true ) ) {
 			return;

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.3/app/DashboardWidget.php /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.4/app/DashboardWidget.php
--- /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.3/app/DashboardWidget.php	2026-05-15 10:46:50.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.4/app/DashboardWidget.php	2026-05-19 12:57:28.000000000 +0000
@@ -41,6 +41,9 @@
 	 * @return void
 	 */
 	public function dashboard_widget() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			return;
+		}
 		// Get options from settings
 		$pushengage_settings = Options::get_site_settings();
 		$hide_widget = $pushengage_settings['misc']['hideDashboardWidget'];
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.3/app/WhatsNew.php /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.4/app/WhatsNew.php
--- /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.3/app/WhatsNew.php	2026-05-15 10:46:50.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/pushengage/4.2.4/app/WhatsNew.php	2026-05-19 12:57:28.000000000 +0000
@@ -52,7 +52,7 @@
 		$pushengage_settings = Options::get_site_settings();
 		$disabled  = ArrayHelper::get( $pushengage_settings, 'dismissed_whats_new_notice', false );
 
-		return 'plugins.php' === $pagenow && ! $disabled;
+		return current_user_can( 'manage_options' ) && 'plugins.php' === $pagenow && ! $disabled;
 	}

Exploit Outline

The vulnerability is exploitable by an authenticated user with Subscriber-level permissions or higher. An attacker can use a valid REST API nonce (extractable from the WordPress dashboard) to send POST requests to endpoints registered by the Abilities API, such as `/wp-json/wp-abilities/v1/call/pushengage/get-plugin-info`. Because the `unwrap_envelope` logic in `AbstractRegistrar.php` was incomplete for certain data shapes, and capability checks were insufficient in `Core.php`, the API response would leak the internal PushEngage 'user' envelope. This envelope contains sensitive information including the account owner's email address, active sites, and external payment provider identifiers (e.g., Stripe IDs).

Check if your site is affected.

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