发现者: Jacob Elliott
2023年7月13日
在VTiger CRM v7.5.0的报表模块中,对报表所选字段的检查不足,这些字段被存储,随后在运行报表时作为二阶SQL注入被重新引入。这允许攻击者从数据库中泄露任意字段,包括用户密码哈希、Web服务API访问密钥以及其他敏感数据。
通过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处的元素,因此检查的字段是Contacts模块中的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;
}