
شرح cve-2024-42327
الهدف: 10.129.231.176
معلومات: أعلم أن هدفي هو خادم Zabbix. حصلت على حساب مستخدم افتراضي لتسجيل الدخول إلى Zabbix: user matthew passwd 96qzn0h2e1k3. هذا الحساب بمستخدم افتراضي، بدون مجموعات أو صلاحيات إضافية.
كالعادة، نبدأ بالتعداد، سنقوم بفحص المنافذ باستخدام nmap.

يُظهر لنا ناتج nmap أن منفذ ssh الافتراضي و apache2 أيضًا على المنفذ الافتراضي. لدينا أيضًا المنفذان 10051 و 10050 يعمل عليهما بعض خدمات Zabbix.
دعنا نصل إلى Zabbix بوضع عنوان IP في رابط المتصفح وعلى منفذ http الافتراضي، المنفذ 80.

هذه هي شاشة تسجيل الدخول إلى Zabbix، سأقوم بتسجيل الدخول بالمستخدم الذي استلمته


في التذييل، وجدت إصدار Zabbix:

باستخدام جوجل، بحثت عما إذا كان هناك أي CVE لهذا الإصدار من Zabbix

بعد وقت طويل من البحث، رأيت أن هذا الإصدار عرضة لـ CVE-2024-42327 الذي يتحدث عن استغلال حقن SQL للحصول على بيانات قاعدة البيانات وتصعيد الامتيازات، و CVE-2024-36467 الذي يتحدث عن السماح بتغيير دور المستخدم إلى مستخدم خارق من خلال إساءة استخدام ضوابط الوصول المفقودة.
https://nvd.nist.gov/vuln/detail/CVE-2024-36467
https://nvd.nist.gov/vuln/detail/CVE-2024-42327
في توثيق Zabbix هناك شرح لكيفية إجراء طلبات HTTP لاستدعاء API.

https://www.zabbix.com/documentation/current/en/manual/api
أرسلت الطلب مستدعيًا appiinfo.version الذي يعلّمنا إياه في التوثيق

والذي أعاد إلينا التالي:
{"jsonrpc":"2.0","result":"7.0.0","id":1}
للاختبار التالي، غيّرت بعض المعاملات في هذا الطلب لإرساله مرة أخرى

في method، غيّرت من appinfo.version إلى user.login وأضفت المعاملين username و password. هذا أيضًا رأيته في توثيق Zabbix.

أعاد إلينا رمزًا (token):
{"jsonrpc":"2.0","result":"9566174b00c9c3ca552abc1a52d670ba","id":1}
بعد مزيد من وقت البحث، قررت الذهاب إلى مستودع Zabbix على GitHub
https://github.com/zabbix/zabbix
بحثت عن CUser ووجدت ملف CUser.php

وجدنا الدالة user.update:
public function update(array $users) {
$this->validateUpdate($users, $db_users);
self::updateForce($users, $db_users);
return ['userids' => array_column($users, 'userid')];
}
لم أجد أي تحقق من الصلاحيات، لذلك قررت تغيير دوري إلى دور مستخدم خارق، عدت إلى الطلب وأجريت التعديلات على الحمولة

أعاد لي خطأ برسالة invalid params.
بعد مزيد من التحليل الطويل للكود وجدنا هذه الدالة
/**
* Additional check to exclude an opportunity to deactivate himself.
*
* @param array $users
* @param array $users[]['usrgrps'] (optional)
*
From this snippet, we understand that we cannot change our roles because our role is checked
from extracting our data from the API token, and verifying against the database if we are that user.
But following the code we see that usrgrps has no validation at all, and therefore can be abused
to add ourselves into multiple groups at once. As long as the group is not disabled and the group
allows GUI access we can abuse this to change our current role with the following command:
User ID 3 is matthew , User group 7 is the Zabbix administrators group and user group 13 is the
Internal group which both hold unrestrictive privileges. The response indicates that the change
was successful:
* @throws APIException
*/
private function checkHimself(array $users) {
foreach ($users as $user) {
if (bccomp($user['userid'], self::$userData['userid']) == 0) {
if (array_key_exists('roleid', $user) && $user['roleid'] !=
self::$userData['roleid']) {
self::exception(ZBX_API_ERROR_PARAMETERS, _('User cannot change
own role.'));
}
if (array_key_exists('usrgrps', $user)) {
$db_usrgrps = DB::select('usrgrp', [
'output' => ['gui_access', 'users_status'],
'usrgrpids' => zbx_objectValues($user['usrgrps'], 'usrgrpid')
]);
foreach ($db_usrgrps as $db_usrgrp) {
if ($db_usrgrp['gui_access'] == GROUP_GUI_ACCESS_DISABLED
|| $db_usrgrp['users_status'] ==
GROUP_STATUS_DISABLED) {
self::exception(ZBX_API_ERROR_PARAMETERS,
_('User cannot add himself to a disabled group or a
group with disabled GUI access.')
);
}
}
}
break;
}
}
}
وفقًا لهذا المقتطف، لا يمكننا تغيير أدوارنا لأن دورنا يتم التحقق منه عن طريق استخراج بياناتنا من رمز API والتحقق في قاعدة البيانات مما إذا كنا هذا المستخدم. لكن بتحليل الكود، نرى أن usrgrps لا يحتوي على أي تحقق، ولهذا النقص في التحقق، يمكن إساءة استخدامه لإضافتنا إلى مجموعات متعددة دفعة واحدة. لا يوجد أي تحقق يمنع المستخدم من إضافة نفسه إلى مجموعات لا ينبغي أن يملك حق الوصول إليها.
لنحاول تصعيد الامتيازات بسبب غياب هذا التحقق، قمت بتعديل الحمولة (payload) وأرسلت الطلب مرة أخرى

userid 3 يشير إلى معرف المستخدم matthew
usrgrps يحتوي على قائمة بمعرفات المجموعات: 13 وهي مجموعة Internal و 7 وهي مجموعة Zabbix administrators. استجابة الخادم تؤكد نجاح العملية:
{"jsonrpc":"2.0","result":{"userids":["3"]},"id":1}
الآن يمكننا استخراج مجموعات المستخدمين للمستخدم الحالي. لنعدّل الطلب ونرسله مرة أخرى.

عند فحص الاستجابة، نرى أن المستخدم ذو المعرف 3 موجود في مجموعتي Internal و Zabbix administrators.
{"jsonrpc":"2.0","result":[{"userid":"1","usrgrps": [{"usrgrpid":"7","name":"Zabbix administrators"}, {"usrgrpid":"13","name":"Internal"}]},{"userid":"2","usrgrps": [{"usrgrpid":"8","name":"Guests"}]},{"userid":"3","usrgrps": [{"usrgrpid":"7","name":"Zabbix administrators"}, {"usrgrpid":"13","name":"Internal"}]}],"id":1}
في سيناريو حيث تم تعيين مجموعة Hosts صالحة لمجموعة Zabbix administrators، يمكنهم الاستفادة من إنشاء العناصر لتفعيل تنفيذ التعليمات البرمجية عن بُعد، وهو ما سيتم تناوله في CVE التالي.
بتحليل الكود المصدري في الفئة CUser مرة أخرى، فحصنا الدالة user.get في السطر 68. يحتوي السطر 108 على تحقق بالكود التالي:
// permission check
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
if (!$options['editable']) {
$sqlParts['from']['users_groups'] = 'users_groups ug';
$sqlParts['where']['uug'] = 'u.userid=ug.userid';
$sqlParts['where'][] = 'ug.usrgrpid IN ('.
' SELECT uug.usrgrpid'.
' FROM users_groups uug'.
' WHERE uug.userid='.self::$userData['userid'].
')';
}
else {
$sqlParts['where'][] = 'u.userid='.self::$userData['userid'];
}
}