
ثغرة حقن SQL مصادق عليها في VTiger CRM مفتوح المصدر الإصدار 7.5
اكتشفها: جاكوب إليوت
13/07/23
في وحدة التقارير في VTiger CRM v7.5.0، يوجد فحص غير كافٍ للحقول المحددة للتقرير والتي يتم تخزينها ثم إعادة إدخالها لاحقًا كحقن SQL من الدرجة الثانية عند تشغيل التقرير. يتيح ذلك للمهاجم تسريب حقول عشوائية من قاعدة البيانات بما في ذلك تجزئات كلمات مرور المستخدمين، ومفاتيح الوصول لواجهة برمجة تطبيقات خدمات الويب، وغيرها من البيانات الحساسة.
بعد المصادقة على CRM، يمكن للمستخدم الانتقال إلى وحدة التقارير وإنشاء تقرير جديد.

نظرًا للطريقة التي يتم بها ربط الجداول، يبدو أن أفضل طريقة هي اختيار وحدة تحتوي على سجلات كوحدة أساسية. اخترت جهات الاتصال، التي احتوت على سجل واحد.

بعد ذلك، يمكن للمستخدم تحديد أي حقول مشروعة من الوحدة الأساسية ومتابعة عملية إنشاء التقرير.

أخيرًا، يمكن للمستخدم النقر على زر حفظ التقرير النهائي، مع اعتراض الاتصالات باستخدام أداة وكيل مثل BurpSuite. في معامل selected_fields، يتم تمرير الحقول المحددة سابقًا إلى دالة الحفظ بالتنسيق:
sql_table:sql_column:label:field_name
في هذه المرحلة، يمكن للمستخدم تعديل sql_table و sql_column إلى أي قيم عشوائية يرغب في تسريبها من قاعدة البيانات. في هذا الإثبات، استخدمت:
vtiger_users:user_name:Contacts_Salutation:salutationtype
و
vtiger_users:user_password:Contacts_First_Name:firstname
بعد إعادة توجيه الطلب المعدل، نُعرض علينا التقرير النهائي الذي يحتوي على الأعمدة المطلوبة من قاعدة البيانات، مما يكشف عن اسم المستخدم وتجزئة كلمة مرور المستخدم المسؤول.

يتم إدخال نقص الفحص المناسب في modules/Reports/ReportRun.php (الأسطر 394-398). يتم تقسيم كل اسم عمود مزود على ":":
$selectedfields = explode(":", $fieldcolname);
ثم إذا لم يكن المستخدم مسؤولاً، يتحقق البرنامج النصي مما إذا كان الحقل موجودًا في مصفوفة الحقول المسموح بها والتي يتم إنشاؤها من الوحدة الأساسية المحددة للتقرير:
!in_array($selectedfields[3], $permitted_fields[$module])
ومع ذلك، تذكر الإدخال الذي تم تقديمه:
vtiger_users:user_name:Contacts_Salutation:salutationtype
نظرًا لأنه يتم فحص "الحقول المسموح بها" مقابل العنصر في الفهرس 3 من المصفوفة، فإن الحقل الذي يتم فحصه هو salutationtype في وحدة جهات الاتصال، والذي لا يعتبر حساسًا وبالتالي يُسمح بتصديره. ومع ذلك، فإن الجدول والعمود المقدمان في أول عنصرين من المصفوفة لا يخضعان لأي تحقق مماثل، مما يؤدي إلى تسريب البيانات.
تم إصلاح هذه المشكلة في هذا الالتزام عن طريق تغيير التحقق من الحقول المحددة بحيث يتم فحصها مقابل الحقول المسموح بها والمشفرة بشكل ثابت في كل وحدة.
public function checkPermission(Vtiger_Request $request) {
parent::checkPermission($request);
$record = $request->get('record');
if ($record) {
$reportModel = Reports_Record_Model::getCleanInstance($record);
if (!$reportModel->isEditable()) {
throw new AppException(vtranslate('LBL_PERMISSION_DENIED'));
}
}
$selectedFields = $request->get('selected_fields');
$groupbyfields = $request->get('groupbyfield');
$fieldsData = array($selectedFields, $groupbyfields);
foreach ($fieldsData as $selectedField){
foreach ($selectedField as $field) {
list($tablename, $colname, $module_field, $fieldname, $single) = split(":", $field);
list($module, $fieldName) = split("_", $module_field, 2);
$moduleModel = Vtiger_Module_Model::getInstance($module);
$fieldModel = Vtiger_Field_Model::getInstance($fieldname, $moduleModel);
if (($fieldModel->table !== $tablename) || ($fieldModel->column !== $colname)) {
throw new AppException(vtranslate('LBL_PERMISSION_DENIED'));
}
}
}
return true;
}