CVE-2026-2991

KiviCare – Clinic & Patient Management System (EHR) <= 4.1.2 - Unauthenticated Authentication Bypass via Social Login Token

highImproper Authentication
7.3
CVSS Score
7.3
CVSS Score
high
Severity
4.1.3
Patched in
8d
Time to patch

Description

The KiviCare – Clinic & Patient Management System (EHR) plugin for WordPress is vulnerable to Authentication Bypass in all versions up to, and including, 4.1.2. This is due to the `patientSocialLogin()` function not verifying the social provider access token before authenticating a user. This makes it possible for unauthenticated attackers to log in as any patient registered on the system by providing only their email address and an arbitrary value for the access token, bypassing all credential verification. The attacker gains access to sensitive medical records, appointments, prescriptions, and billing information (PII/PHI breach). Additionally, authentication cookies are set before the role check, meaning the auth cookies for non-patient users (including administrators) are also set in the HTTP response headers, even though a 403 response is returned.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
Low
Integrity
Low
Availability

Technical Details

Affected versions<=4.1.2
PublishedMarch 17, 2026
Last updatedMarch 24, 2026

What Changed in the Fix

Changes introduced in v4.1.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-2991 KiviCare Authentication Bypass ## 1. Vulnerability Summary The **KiviCare – Clinic & Patient Management System (EHR)** plugin (<= 4.1.2) contains an authentication bypass vulnerability in its REST API. The vulnerability exists within the `patientSocialLog…

Show full research plan

Exploitation Research Plan: CVE-2026-2991 KiviCare Authentication Bypass

1. Vulnerability Summary

The KiviCare – Clinic & Patient Management System (EHR) plugin (<= 4.1.2) contains an authentication bypass vulnerability in its REST API. The vulnerability exists within the patientSocialLogin() function of the App\controllers\api\AuthController class.

The function is designed to authenticate patients using social media providers (Google, Facebook, etc.). However, it fails to perform a server-side verification of the provided access_token with the social provider. Instead, it retrieves a user based on the provided email address and immediately issues authentication cookies using wp_set_auth_cookie(). While it subsequently checks if the user has the 'patient' role and returns a 403 Forbidden response for non-patients (like administrators), the authentication cookies are already included in the HTTP response headers, allowing an attacker to hijack any user session, including administrator accounts.

2. Attack Vector Analysis

  • Endpoint: POST /wp-json/kivicare/v1/auth/patient-social-login (inferred prefix kivicare/v1 and controller route auth).
  • Target Function: patientSocialLogin() in App\controllers\api\AuthController.
  • Authentication: Unauthenticated (Publicly accessible).
  • Parameters:
    • email: The email address of the target user (e.g., a known patient or the site administrator).
    • accessToken: An arbitrary string value (as the token verification is skipped/missing).
    • provider: (Optional/Inferred) The social provider name (e.g., 'google').
  • Preconditions:
    • The target email must exist in the WordPress database.
    • The REST API must be active (default in WordPress).

3. Code Flow

  1. Request Reception: A POST request is sent to kivicare/v1/auth/patient-social-login.
  2. Controller Routing: KCRestAPI.php routes the request to AuthController.
  3. User Retrieval: patientSocialLogin() receives the email and accessToken parameters.
  4. Bypass Point: The code retrieves the WP_User object by the provided email. It proceeds to call wp_set_auth_cookie($user->ID) before verifying the validity of the accessToken or the user's role.
  5. Role Verification: After the cookies are set in the response context, the function checks if the user has the patient role.
  6. Sink/Leak:
    • If the user is a patient, a 200 OK response is returned with patient data.
    • If the user is NOT a patient (e.g., an Administrator), a 403 Forbidden error is returned. However, because wp_set_auth_cookie was already called, the response headers contain Set-Cookie headers for the target user.

4. Nonce Acquisition Strategy

Based on the nature of login/authentication endpoints in KiviCare (and typical WordPress REST API design for Auth controllers), no nonce is required for this endpoint.

  • Authentication endpoints must be reachable by logged-out users who do not yet have a session/nonce.
  • AuthController methods typically have 'permission_callback' => '__return_true' for login actions.

5. Exploitation Strategy

Step 1: Exploit a Patient Account (Direct Access)

  1. Identify a valid patient email.
  2. Send the malicious login request.
  3. Capture the authentication cookies and access the patient dashboard.

Step 2: Exploit an Administrator Account (Header Leakage)

  1. Identify the administrator's email.
  2. Send the malicious login request.
  3. Observe the 403 Forbidden status.
  4. Extract the wordpress_logged_in_[hash] cookie from the Set-Cookie response headers.
  5. Use the extracted cookie to access the WordPress admin area (/wp-admin/).

Target Request (Example):

POST /wp-json/kivicare/v1/auth/patient-social-login HTTP/1.1
Content-Type: application/json

{
    "email": "admin@example.com",
    "accessToken": "pwned",
    "provider": "google"
}

6. Test Data Setup

  1. Create Administrator: wp user create exploit_admin admin@kivicare.local --role=administrator --user_pass=password123
  2. Create Patient:
    • Use KiviCare's built-in registration or WP-CLI: wp user create exploit_patient patient@kivicare.local --role=patient (Note: Ensure the role slug is patient as used by the plugin).
  3. Verify Plugin Version: Ensure KiviCare <= 4.1.2 is active.

7. Expected Results

  • For Patient Email: Response 200 OK. The JSON body contains user details. Set-Cookie headers provide valid session cookies.
  • For Admin Email: Response 403 Forbidden. The JSON body contains an error message (e.g., "Only patients can login through this method"). Crucially, the HTTP headers still contain Set-Cookie: wordpress_logged_in_....

8. Verification Steps

  1. Capture Cookies: Extract the wordpress_logged_in_ cookie from the response of the attack against the administrator email.
  2. Authenticated Request: Use the http_request tool to call a restricted admin endpoint using the captured cookie:
    • Endpoint: GET /wp-json/kivicare/v1/config/system-status (registered in ConfigController.php with checkAdminPermission).
  3. Confirm Success: If the response returns 200 OK and system status data (PHP version, database info), the bypass is confirmed.

9. Alternative Approaches

If accessToken is not the correct key, try:

  • access_token
  • token
  • social_token

If the route auth/patient-social-login returns 404, verify the registered routes in the plugin's REST initialization by searching for the string "social" in app/controllers/api/AuthController.php (as the provided snippet was truncated). Common variants:

  • /kivicare/v1/auth/social-login
  • /kivicare/v1/auth/patient-login
Research Findings
Static analysis — not yet PoC-verified

Summary

The KiviCare plugin for WordPress is vulnerable to an authentication bypass because the `patientSocialLogin()` function fails to verify social provider access tokens. Attackers can provide a target user's email and an arbitrary token to obtain a valid authentication session. Furthermore, the plugin issues authentication cookies for all user roles, including administrators, before verifying if the account has the required 'patient' role, allowing for full site takeover via response header leakage.

Vulnerable Code

// app/controllers/api/AuthController.php (Route registration at line 283)
$this->registerRoute('/' . $this->route . '/patient/social-login', [
    'methods' => WP_REST_Server::CREATABLE,
    'callback' => [$this, 'patientSocialLogin'],
    'permission_callback' => '__return_true',
    'args' => $this->getSocialLoginEndpointArgs()
]);

---

// app/controllers/api/AuthController.php:1867
public function patientSocialLogin(WP_REST_Request $request): WP_REST_Response
{
    $params = $request->get_params();
    $login_type = strtolower($params['login_type']);
    $email = !empty($params['email']) ? sanitize_email($params['email']) : '';
    $contact_number = !empty($params['contact_number']) ? sanitize_text_field($params['contact_number']) : '';
    $access_token = !empty($params['password']) ? $params['password'] : ''; // Access token from social provider
    // ... (logic to retrieve user by email or phone)
    $user = get_user_by('email', $email);
    
    // ... (logic sets $user_id and processes profile updates without verifying $access_token with a provider)

    // Log in the user - authentication cookies are set before role validation
    wp_clear_auth_cookie();
    wp_set_auth_cookie($user_id);
    
    // Role check occurs AFTER authentication cookies are already set in the response headers
    if (!in_array('patient', $user->roles)) {
        return $this->response(null, 'Only patients can login through this method', false, 403);
    }
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.1.2/app/controllers/api/AuthController.php /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.1.3/app/controllers/api/AuthController.php
--- /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.1.2/app/controllers/api/AuthController.php	2026-02-16 15:47:10.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/kivicare-clinic-management-system/4.1.3/app/controllers/api/AuthController.php	2026-02-23 08:43:34.000000000 +0000
@@ -279,14 +279,6 @@
             'permission_callback' => 'is_user_logged_in',
             'args' => []
         ]);
-
-        // Patient social login endpoint
-        $this->registerRoute('/' . $this->route . '/patient/social-login', [
-            'methods' => WP_REST_Server::CREATABLE,
-            'callback' => [$this, 'patientSocialLogin'],
-            'permission_callback' => '__return_true',
-            'args' => $this->getSocialLoginEndpointArgs()
-        ]);
     }
 
     private function getLoginEndpointArgs()

Exploit Outline

The exploit target is the REST API endpoint `/wp-json/kivicare/v1/auth/patient/social-login`. 1. Identify the target email address (e.g., the site administrator or a specific patient). 2. Send an unauthenticated POST request to the endpoint with the following JSON body: - `email`: The target's email address. - `password`: An arbitrary string (the application erroneously treats this as the social provider access token and fails to verify it). - `login_type`: Either 'google' or 'apple'. 3. If the target is a patient, the server returns 200 OK and includes authentication cookies in the response headers. 4. If the target is an administrator, the server returns 403 Forbidden. However, because `wp_set_auth_cookie()` is called before the role check, the response headers will still contain the `Set-Cookie` directives for the administrator's session. 5. Extract the `wordpress_logged_in_` cookie from the response headers and use it to access restricted areas like `/wp-admin/`.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.