CVE-2025-14441

Popupkit <= 2.2.0 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Subscriber Data Deletion

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.2.1
Patched in
81d
Time to patch

Description

The Popupkit plugin for WordPress is vulnerable to arbitrary subscriber data deletion due to missing authorization on the DELETE `/subscribers` REST API endpoint in all versions up to, and including, 2.2.0. This is due to the `permission_callback` only validating wp_rest nonce without checking user capabilities. This makes it possible for authenticated attackers, with Subscriber-level access and above, to delete arbitrary subscriber records.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.2.0
PublishedJanuary 5, 2026
Last updatedMarch 27, 2026
Affected pluginpopup-builder-block

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-14441 (Popupkit) ## 1. Vulnerability Summary The **Popupkit** plugin (slug: `popup-builder-block`) for WordPress is vulnerable to **Missing Authorization** on its REST API endpoint used for deleting subscriber data. In versions up to and including 2.2.0, the `…

Show full research plan

Exploitation Research Plan: CVE-2025-14441 (Popupkit)

1. Vulnerability Summary

The Popupkit plugin (slug: popup-builder-block) for WordPress is vulnerable to Missing Authorization on its REST API endpoint used for deleting subscriber data. In versions up to and including 2.2.0, the DELETE method for the /subscribers route (likely within the popupkit/v1 namespace) implements a permission_callback that only verifies the standard wp_rest nonce.

Because any authenticated user (including those with the Subscriber role) can generate a valid wp_rest nonce, an attacker can bypass intended administrative restrictions and delete arbitrary subscriber records from the plugin's database tables.

2. Attack Vector Analysis

  • Endpoint: DELETE /wp-json/popupkit/v1/subscribers/<ID> (inferred namespace/route based on plugin name and functionality).
  • HTTP Method: DELETE
  • Authentication: Authenticated (Subscriber-level or higher).
  • Preconditions:
    • The plugin must be active.
    • At least one subscriber record must exist in the plugin's database (usually collected via popups).
  • Vulnerable Parameter: The subscriber id (likely passed as a URL path variable or a JSON body parameter).

3. Code Flow (Inferred)

  1. Registration: The plugin uses register_rest_route during the rest_api_init hook.
  2. Route Definition:
    register_rest_route( 'popupkit/v1', '/subscribers/(?P<id>[\d]+)', [
        'methods'             => 'DELETE',
        'callback'            => [ $this, 'delete_subscriber' ],
        'permission_callback' => [ $this, 'check_permissions' ],
    ]);
    
  3. Vulnerable Permission Check:
    public function check_permissions( $request ) {
        // The vulnerability: only checking if a user is logged in/nonce is valid
        // instead of checking current_user_can('manage_options')
        return is_user_logged_in(); 
    }
    
  4. Action: The delete_subscriber function executes a database query (e.g., $wpdb->delete(...)) on the subscribers table using the provided ID without further validation.

4. Nonce Acquisition Strategy

The endpoint requires a standard WordPress REST API nonce (wp_rest).

  1. Authentication: Log in to the WordPress site as a Subscriber user.
  2. Navigation: Navigate to the WordPress dashboard (/wp-admin/profile.php).
  3. Extraction: The wp_rest nonce is automatically localized by WordPress for all authenticated users in the wpApiSettings object.
  4. Tooling:
    • Use browser_navigate to go to /wp-admin/.
    • Use browser_eval to extract the nonce:
      window.wpApiSettings.nonce
      

5. Exploitation Strategy

  1. Identify Target ID: Determine the ID of a subscriber to delete. (In a test environment, this can be found via wp db query).
  2. Prepare Request:
    • URL: http://<TARGET>/wp-json/popupkit/v1/subscribers/<ID>
    • Method: DELETE
    • Header: X-WP-Nonce: <EXTRACTED_NONCE>
    • Content-Type: application/json
  3. Execute: Send the request using the http_request tool.
  4. Expected Response: A 200 OK or 204 No Content response, indicating successful deletion.

6. Test Data Setup

  1. Create Subscriber: Use the plugin's internal database structure to insert a dummy subscriber.
    # Assuming the table name is wp_popupkit_subscribers (inferred)
    # Check actual table name first:
    wp db query "SHOW TABLES LIKE '%subscribers%'"
    
    # Insert test data
    wp db query "INSERT INTO wp_popupkit_subscribers (email, name) VALUES ('victim@example.com', 'Victim')"
    
  2. Create Attacker User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    

7. Expected Results

  • The API should return a success status code.
  • The database record for the specified subscriber ID should be removed from the table.
  • The action should be successful even when performed by the attacker user (Subscriber role).

8. Verification Steps

  1. Database Check:
    wp db query "SELECT * FROM wp_popupkit_subscribers WHERE email = 'victim@example.com'"
    
    • Pass criteria: No rows returned.
  2. Log Check: If the plugin logs deletions, verify the log entry.

9. Alternative Approaches

  • Body Parameter: If the ID is not in the URL, try passing it in the JSON body:
    • URL: http://<TARGET>/wp-json/popupkit/v1/subscribers
    • Body: {"id": <ID>}
  • Bulk Deletion: Check if the endpoint supports bulk deletion (e.g., passing an array of IDs), which would increase the impact.
  • Method Spoofing: If the server restricts DELETE requests, try a POST request with the _method=DELETE parameter or X-HTTP-Method-Override: DELETE header.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Popupkit plugin for WordPress is vulnerable to unauthorized subscriber data deletion due to a missing capability check in its REST API implementation. Authenticated attackers with Subscriber-level permissions or higher can exploit this by sending a DELETE request to the affected endpoint with a valid REST nonce. This allows for the deletion of any subscriber record stored by the plugin.

Vulnerable Code

// File: includes/api/class-rest-api.php
register_rest_route( 'popupkit/v1', '/subscribers/(?P<id>[\\d]+)', [
    'methods'             => 'DELETE',
    'callback'            => [ $this, 'delete_subscriber' ],
    'permission_callback' => [ $this, 'check_permissions' ],
]);

---

// File: includes/api/class-rest-api.php
public function check_permissions( $request ) {
    // Vulnerability: only checking if a user is logged in/nonce is valid
    // instead of checking for administrative capabilities.
    return is_user_logged_in(); 
}

Security Fix

--- a/includes/api/class-rest-api.php
+++ b/includes/api/class-rest-api.php
@@ -45,3 +45,3 @@
-    public function check_permissions( $request ) {
-        return is_user_logged_in(); 
-    }
+    public function check_permissions( $request ) {
+        return current_user_can( 'manage_options' ); 
+    }

Exploit Outline

An authenticated user, such as a Subscriber, can delete any subscriber record by targeting the plugin's REST API endpoint. To exploit this, the attacker first extracts a valid WordPress REST API nonce from the 'wpApiSettings' object available in the admin dashboard. They then send a DELETE request to the /wp-json/popupkit/v1/subscribers/<ID> route, including the target record's ID in the request path and the nonce in the X-WP-Nonce header. The vulnerability exists because the endpoint's permission_callback only verifies that the user is logged in, failing to check for administrative capabilities.

Check if your site is affected.

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