VTiger 开源 CRM v7.5 中的认证远程代码执行漏洞
当前发行的 VTiger CRM 开源版本 7.5.0 的 Users 模块中存在一个漏洞,允许经过认证的攻击者向 config.inc.php 写入并执行任意 PHP 代码。
当用户正常认证到 VTiger 时,系统会检查其设置状态,以判断是否为尚未完全配置的新用户。同时也会检查是否为第一个用户(即应配置某些全局属性的管理员)。如果是,则会被重定向到 Users 模块中的 UserSetup 操作。
$userSetupStatus = Users_CRMSetup::getUserSetupStatus($focus->id);
if ($userSetupStatus) {
$user = $focus->retrieve_entity_info($focus->id, 'Users');
$isFirstUser = Users_CRMSetup::isFirstUser($user);
if($isFirstUser) {
header('Location: index.php?module=Users&action=UserSetup');
}
/modules/Users/Authenticate.php, 第 112 行
在该操作中,会检查用户是否传递了全局货币设置(默认值为 "USA, Dollars"),并通过 updateBaseCurrency 函数进行保存。
//Handling the System Setup
$currencyName = $request->get('currency_name');
if(!empty($currencyName)) $userModuleModel->updateBaseCurrency($currencyName);
$userModuleModel->insertEntryIntoCRMSetup($userRecordModel->getId());
//End
/modules/Users/actions/UserSetupSave.php, 第 28 行
在 updateBaseCurrency 函数中,提供的值被设置为数据库中整个系统的全局货币设置,同时还会调用 updateConfigFile 函数,该函数打开配置文件并使用 PHP 的 str_replace 将现有值替换为提供的值。
public function updateConfigFile($currencyName) {
$currencyName = '$currency_name = \''.$currencyName.'\'';
//Updating in config inc file
$filename = 'config.inc.php';
if (file_exists($filename)) {
$contents = file_get_contents($filename);
$currentBaseCurrenyName = $this->getBaseCurrencyName();
$contents = str_replace('$currency_name =
\''.$currentBaseCurrenyName.'\'', $currencyName, $contents);
file_put_contents($filename, $contents);
}
}
/modules/Users/models/Module.php, 第 148 行
当前版本的 VTiger CRM 中的用户设置操作并未检查用户是否已实际完成设置,或是否有权限更改全局变量。它只是假设,如果你访问该页面,你就是那个应该进行配置的人。因此,通过使用现有的会话 cookie 和从任何已认证页面加载中获取的 CSRF 令牌,恶意用户可以 POST 提交类似以下的货币值:
USA, Dollars';@passthru($_GET['cmd']);//
该值随后被盲目地写入 config.inc.php 文件,形式如下:
//Master currency name
$currency_name = 'USA, Dollars';@passthru($_GET['cmd']);//';
由于配置文件在每个页面加载时都会被加载,因此恶意用户只需将 Unix 命令作为 cmd 参数传递,即可在页面顶部看到输出。

该问题已在此提交中得到修复,其中增加了额外的验证,确保用户无法输入恶意的货币名称,而是从数据库表中获取该值。
public function updateBaseCurrency($currencyName) {
$db = PearDatabase::getInstance();
$result = $db->pquery('SELECT currency_code, currency_symbol, currency_name FROM vtiger_currencies WHERE currency_name = ?', array($currencyName));
$num_rows = $db->num_rows($result);
if ($num_rows > 0) {
$currency_code = decode_html($db->query_result($result, 0, 'currency_code'));
$currency_symbol = decode_html($db->query_result($result, 0,'currency_symbol'));
$currencyName = decode_html($db->query_result($result, 0, 'currency_name')); // 从表中重写实际值。
} else {
// 无效的货币名称。
return;
}
$this->updateConfigFile($currencyName);
//更新数据库
$query = 'UPDATE vtiger_currency_info SET currency_name = ?, currency_code = ?, currency_symbol = ? WHERE id = ?';
$params = array($currencyName, $currency_code, $currency_symbol, '1');
$db->pquery($query, $params);
}