
Уязвимость аутентифицированной SQL-инъекции в VTiger Open Source CRM v7.5
Обнаружено: Jacob Elliott
13.07.23
В модуле Reports в VTiger CRM v7.5.0 недостаточно проверяются выбранные поля для отчета, которые сохраняются, а затем повторно используются при запуске отчета, что приводит к SQL-инъекции второго порядка. Это позволяет злоумышленнику извлечь произвольные поля из базы данных, включая хэши паролей пользователей, ключи доступа к веб-сервисам и другие конфиденциальные данные.
После аутентификации в CRM пользователь может перейти в модуль Reports и создать новый отчет.

Из-за способа соединения таблиц лучше всего в качестве основного модуля выбрать модуль, в котором есть записи. Я выбрал Contacts, где была одна запись.

Затем пользователь может выбрать любые допустимые поля из основного модуля и продолжить процесс создания отчета.

Наконец, пользователь может нажать кнопку для сохранения итогового отчета, перехватывая соединения с помощью такого прокси-инструмента, как BurpSuite. В параметре selected_fields ранее выбранные поля передаются в функцию сохранения в формате:
sql_table:sql_column:label:field_name
На этом этапе пользователь может изменить sql_table и sql_column на любые произвольные значения, которые хочет извлечь из базы данных. Для этой POC я использовал:
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 в модуле Contacts, которое не считается конфиденциальным и потому разрешено для экспорта. Однако таблица и столбец, указанные в первых двух элементах массива, такой проверке не подвергаются, что приводит к утечке данных.
Проблема была исправлена в этом коммите путем изменения проверки выбранных полей: теперь они проверяются на соответствие разрешенным полям, жестко заданным в каждом модуле.
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;
}