KiviCare – Clinic & Patient Management System (EHR) <= 3.6.15 - Missing Authorization to Unauthenticated Limited Arbitrary File Upload
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:NTechnical Details
<=3.6.15Source Code
WordPress.org SVNThis 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) orupload_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
noprivhandler callscheck_ajax_referer.
3. Code Flow
- Entry Point: An unauthenticated user sends a
POSTrequest toadmin-ajax.php?action=kivicare_upload_medical_report. - Hook Trigger: WordPress triggers the hook
wp_ajax_nopriv_kivicare_upload_medical_report. - Function Call: The handler function
uploadMedicalReport()is executed. - Authorization Check: The function fails to check if the user is a registered patient or medical staff.
- Nonce Verification (Potential Sink): The function likely calls
check_ajax_referer('kivicare_ajax_nonce', 'nonce'). - File Processing: The function accesses
$_FILES, validates the extension against a whitelist (txt, pdf), and useswp_handle_upload()ormove_uploaded_file()to save the file towp-content/uploads/kivicare/or a similar directory. - 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):
- Identify Localization: Search the codebase for
wp_localize_script. KiviCare typically localizes data into a JavaScript object namedkivicare_objorkivi_vars. - 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]. - Setup Page:
wp post create --post_type=page --post_status=publish --post_title="Upload Test" --post_content='[kivicare-patient-dashboard]'
- 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.
- Navigate to the newly created page using
5. Exploitation Strategy
- Step 1: Discovery:
- Search for the registration of
uploadMedicalReportto confirm the exact AJAX action string. grep -rn "uploadMedicalReport" wp-content/plugins/kivicare-clinic-management-system/
- Search for the registration of
- Step 2: Nonce Extraction: (Follow the strategy in Section 4).
- Step 3: Construct Payload:
- Create a text file
poc.txtwith the content:CVE-2026-0927 PoC - Unauthorized Upload.
- Create a text file
- Step 4: Execute Upload:
- Use
http_requestto send amultipart/form-dataPOST 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]
- Use
- Step 5: Parse Response:
- Look for a
success: trueresponse and adata.urlordata.file_pathfield.
- Look for a
6. Test Data Setup
- Activate Plugin: Ensure
kivicare-clinic-management-systemis active. - Create Extraction Page:
wp post create --post_type=page --post_status=publish --post_content='[kivicare-patient-dashboard]' --post_title='SecurityResearch'
- 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"}}
- Example:
- File Persistence: The file
poc.txtshould be accessible via a direct GET request to the returned URL.
8. Verification Steps
- 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"
- Filesystem Check: Use WP-CLI to verify the file exists on disk.
find wp-content/uploads/ -name "poc.txt"
- 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
.txtis blocked by server-side MIME sniffing, attempt with a valid PDF header:- Content:
%PDF-1.4\n1 0 obj\n<< /Title (PoC) >>\nendobj
- Content:
- REST API: Check if KiviCare exposes the same function via the WordPress REST API (
/wp-json/kivicare/v1/...). Usegrep -r "register_rest_route" .to find alternative entry points. - Default Nonce: Check if the function uses a generic nonce like
wp_restor-1. Ifwp_verify_nonce($nonce, -1)is used, any valid nonce from the frontend will work.
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
@@ -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.