KiviCare – Clinic & Patient Management System (EHR) <= 4.1.2 - Unauthenticated Authentication Bypass via Social Login Token
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:LTechnical Details
<=4.1.2What Changed in the Fix
Changes introduced in v4.1.3
Source Code
WordPress.org SVN# 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 prefixkivicare/v1and controller routeauth). - Target Function:
patientSocialLogin()inApp\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
- Request Reception: A
POSTrequest is sent tokivicare/v1/auth/patient-social-login. - Controller Routing:
KCRestAPI.phproutes the request toAuthController. - User Retrieval:
patientSocialLogin()receives theemailandaccessTokenparameters. - Bypass Point: The code retrieves the
WP_Userobject by the providedemail. It proceeds to callwp_set_auth_cookie($user->ID)before verifying the validity of theaccessTokenor the user's role. - Role Verification: After the cookies are set in the response context, the function checks if the user has the
patientrole. - Sink/Leak:
- If the user is a patient, a
200 OKresponse is returned with patient data. - If the user is NOT a patient (e.g., an Administrator), a
403 Forbiddenerror is returned. However, becausewp_set_auth_cookiewas already called, the response headers containSet-Cookieheaders for the target user.
- If the user is a patient, a
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.
AuthControllermethods typically have'permission_callback' => '__return_true'for login actions.
5. Exploitation Strategy
Step 1: Exploit a Patient Account (Direct Access)
- Identify a valid patient email.
- Send the malicious login request.
- Capture the authentication cookies and access the patient dashboard.
Step 2: Exploit an Administrator Account (Header Leakage)
- Identify the administrator's email.
- Send the malicious login request.
- Observe the
403 Forbiddenstatus. - Extract the
wordpress_logged_in_[hash]cookie from theSet-Cookieresponse headers. - 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
- Create Administrator:
wp user create exploit_admin admin@kivicare.local --role=administrator --user_pass=password123 - 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 ispatientas used by the plugin).
- Use KiviCare's built-in registration or WP-CLI:
- 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-Cookieheaders 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 containSet-Cookie: wordpress_logged_in_....
8. Verification Steps
- Capture Cookies: Extract the
wordpress_logged_in_cookie from the response of the attack against the administrator email. - Authenticated Request: Use the
http_requesttool to call a restricted admin endpoint using the captured cookie:- Endpoint:
GET /wp-json/kivicare/v1/config/system-status(registered inConfigController.phpwithcheckAdminPermission).
- Endpoint:
- Confirm Success: If the response returns
200 OKand system status data (PHP version, database info), the bypass is confirmed.
9. Alternative Approaches
If accessToken is not the correct key, try:
access_tokentokensocial_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
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
@@ -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.