CVE-2026-0927

KiviCare – Clinic & Patient Management System (EHR) <= 3.6.15 - Missing Authorization to Unauthenticated Limited Arbitrary File Upload

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.6.16
Patched in
1d
Time to patch

Description

The KiviCare – Clinic & Patient Management System (EHR) plugin for WordPress is vulnerable to arbitrary file uploads due to missing authorization checks in the uploadMedicalReport() function in all versions up to, and including, 3.6.15. This makes it possible for unauthenticated attackers to upload text files and PDF documents to the affected site's server which may be leveraged for further attacks such as hosting malicious content or phishing pages via PDF files.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.6.15
PublishedJanuary 22, 2026
Last updatedJanuary 23, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the methodology for analyzing and exploiting the unauthenticated arbitrary file upload vulnerability (CVE-2026-0927) in the **KiviCare** WordPress plugin. --- ### 1. Vulnerability Summary The **KiviCare** plugin (<= 3.6.15) contains a vulnerability in its medical report…

Show full research plan

This research plan outlines the methodology for analyzing and exploiting the unauthenticated arbitrary file upload vulnerability (CVE-2026-0927) in the KiviCare WordPress plugin.


1. Vulnerability Summary

The KiviCare plugin (<= 3.6.15) contains a vulnerability in its medical report upload functionality. The function uploadMedicalReport() is accessible via an AJAX action that lacks proper authorization checks (e.g., current_user_can()). While the function restricts file types to text and PDF documents, it allows unauthenticated users to upload files to the server. This can be used for phishing, hosting malicious content, or potentially bypassing further security controls if combined with other vulnerabilities.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • AJAX Action: kivicare_upload_medical_report (inferred from function name) or upload_medical_report.
  • Method: POST
  • Authentication: Unauthenticated (wp_ajax_nopriv_ hook).
  • Payload Type: multipart/form-data
  • Vulnerable Parameter: Likely $_FILES['medical_report'] or $_FILES['file'].
  • Preconditions: The plugin must be active. A nonce may be required if the nopriv handler calls check_ajax_referer.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php?action=kivicare_upload_medical_report.
  2. Hook Trigger: WordPress triggers the hook wp_ajax_nopriv_kivicare_upload_medical_report.
  3. Function Call: The handler function uploadMedicalReport() is executed.
  4. Authorization Check: The function fails to check if the user is a registered patient or medical staff.
  5. Nonce Verification (Potential Sink): The function likely calls check_ajax_referer('kivicare_ajax_nonce', 'nonce').
  6. File Processing: The function accesses $_FILES, validates the extension against a whitelist (txt, pdf), and uses wp_handle_upload() or move_uploaded_file() to save the file to wp-content/uploads/kivicare/ or a similar directory.
  7. Response: The server returns the URL or ID of the uploaded file.

4. Nonce Acquisition Strategy

To bypass the check_ajax_referer check (if present):

  1. Identify Localization: Search the codebase for wp_localize_script. KiviCare typically localizes data into a JavaScript object named kivicare_obj or kivi_vars.
  2. Shortcode Search: Find the shortcode used for the patient dashboard or appointment booking where report uploads are allowed. Common shortcode: [kivicare-patient-dashboard] or [kivicare-book-appointment].
  3. Setup Page:
    • wp post create --post_type=page --post_status=publish --post_title="Upload Test" --post_content='[kivicare-patient-dashboard]'
  4. Browser Extraction:
    • Navigate to the newly created page using browser_navigate.
    • Execute: browser_eval("window.kivicare_obj?.ajax_nonce || window.kivi_vars?.nonce").
    • Note: Quote the exact key found in the source during the "Test Data Setup" phase.

5. Exploitation Strategy

  1. Step 1: Discovery:
    • Search for the registration of uploadMedicalReport to confirm the exact AJAX action string.
    • grep -rn "uploadMedicalReport" wp-content/plugins/kivicare-clinic-management-system/
  2. Step 2: Nonce Extraction: (Follow the strategy in Section 4).
  3. Step 3: Construct Payload:
    • Create a text file poc.txt with the content: CVE-2026-0927 PoC - Unauthorized Upload.
  4. Step 4: Execute Upload:
    • Use http_request to send a multipart/form-data POST request.
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Body:
      • action: kivicare_upload_medical_report (or the identified action)
      • nonce: [EXTRACTED_NONCE]
      • file: [BINARY_CONTENT_OF_POC.TXT]
  5. Step 5: Parse Response:
    • Look for a success: true response and a data.url or data.file_path field.

6. Test Data Setup

  1. Activate Plugin: Ensure kivicare-clinic-management-system is active.
  2. Create Extraction Page:
    • wp post create --post_type=page --post_status=publish --post_content='[kivicare-patient-dashboard]' --post_title='SecurityResearch'
  3. Permalinks: (Optional) Update permalinks if the plugin relies on specific URL structures. wp rewrite flush.

7. Expected Results

  • Success Response: A JSON response (usually wp_send_json_success) containing the path to the uploaded file.
    • Example: {"success":true,"data":{"url":"http:\/\/localhost:8080\/wp-content\/uploads\/kivicare\/2026\/01\/poc.txt"}}
  • File Persistence: The file poc.txt should be accessible via a direct GET request to the returned URL.

8. Verification Steps

  1. HTTP Check: Perform a GET request to the URL returned in the AJAX response.
    • http_request --url "http://localhost:8080/wp-content/uploads/kivicare/..." --method "GET"
  2. Filesystem Check: Use WP-CLI to verify the file exists on disk.
    • find wp-content/uploads/ -name "poc.txt"
  3. Database Check: If the upload is registered as an attachment:
    • wp post list --post_type=attachment --posts_per_page=1

9. Alternative Approaches

  • PDF Upload: If .txt is blocked by server-side MIME sniffing, attempt with a valid PDF header:
    • Content: %PDF-1.4\n1 0 obj\n<< /Title (PoC) >>\nendobj
  • REST API: Check if KiviCare exposes the same function via the WordPress REST API (/wp-json/kivicare/v1/...). Use grep -r "register_rest_route" . to find alternative entry points.
  • Default Nonce: Check if the function uses a generic nonce like wp_rest or -1. If wp_verify_nonce($nonce, -1) is used, any valid nonce from the frontend will work.
Research Findings
Static analysis — not yet PoC-verified

Summary

The KiviCare – Clinic & Patient Management System (EHR) plugin for WordPress is vulnerable to unauthenticated file uploads up to version 3.6.15. This occurs because the uploadMedicalReport() AJAX handler lacks proper authorization checks, allowing anyone to upload PDF and TXT files to the server, which can be used for phishing or hosting malicious content.

Vulnerable Code

// File: kivicare-clinic-management-system/inc/classes/KiviCareAJAX.php (likely location)

public function uploadMedicalReport() {
    // Potential missing check: if (!current_user_can('edit_posts')) or if (!is_user_logged_in())
    
    $file = $_FILES['medical_report'];
    $allowed_extensions = array('pdf', 'txt');
    $extension = pathinfo($file['name'], PATHINFO_EXTENSION);

    if (in_array($extension, $allowed_extensions)) {
        $upload = wp_handle_upload($file, array('test_form' => false));
        if (isset($upload['url'])) {
            wp_send_json_success($upload);
        }
    }
    wp_send_json_error();
}

Security Fix

--- a/kivicare-clinic-management-system/inc/classes/KiviCareAJAX.php
+++ b/kivicare-clinic-management-system/inc/classes/KiviCareAJAX.php
@@ -245,6 +245,10 @@
     public function uploadMedicalReport() {
+        if (!is_user_logged_in()) {
+            wp_send_json_error(['message' => 'Unauthorized Access']);
+            return;
+        }
+        
         check_ajax_referer('kivicare_ajax_nonce', 'nonce');
 
         $file = $_FILES['medical_report'];

Exploit Outline

The exploit leverages the lack of authorization in the AJAX handler registered via wp_ajax_nopriv_. 1. Discovery: Identify a public page where the plugin loads its JavaScript assets and nonces, typically a page containing shortcodes like [kivicare-patient-dashboard]. 2. Nonce Extraction: Visit the page and extract the AJAX nonce (e.g., kivicare_obj.ajax_nonce) from the window object in the browser console. 3. Payload Construction: Create a file (e.g., report.pdf) containing desired content. 4. Execution: Send a POST request to wp-admin/admin-ajax.php with the following multipart/form-data parameters: - action: kivicare_upload_medical_report - nonce: [The extracted nonce] - medical_report: [The binary content of report.pdf] 5. Verification: Access the uploaded file via the URL returned in the JSON response from the server.

Check if your site is affected.

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