
Vulnerabilità di iniezione SQL autenticata in VTiger Open Source CRM v7.5
Scoperta da: Jacob Elliott
07/13/23
Nel modulo Reports di VTiger CRM v7.5.0, il controllo dei campi selezionati per il report è insufficiente: tali campi vengono salvati e successivamente reintrodotti come SQL Injection di secondo ordine quando il report viene eseguito. Ciò consente all'attaccante di estrarre campi arbitrari dal database, inclusi hash delle password utente, chiavi di accesso API del webservice e altri dati sensibili.
Dopo l'autenticazione al CRM, l'utente può accedere al modulo Reports e creare un nuovo report.

A causa del modo in cui le tabelle vengono unite, sembra funzionare meglio scegliere come modulo primario un modulo che contiene record. Ho scelto Contacts, che conteneva un record.

Successivamente, l'utente può selezionare qualsiasi campo legittimo dal modulo primario e continuare il processo di creazione del report.

Infine, l'utente può fare clic sul pulsante per salvare il report finale, intercettando le connessioni con uno strumento proxy come BurpSuite. Nel parametro selected_fields, i campi precedentemente selezionati vengono passati alla funzione di salvataggio nel formato:
sql_table:sql_column:label:field_name
A questo punto, l'utente può modificare sql_table e sql_column in qualsiasi valore arbitrario desideri estrarre dal database. Per questo POC, ho usato:
vtiger_users:user_name:Contacts_Salutation:salutationtype
e
vtiger_users:user_password:Contacts_First_Name:firstname
Dopo aver inoltrato la richiesta modificata, ci viene presentato il report finale che contiene le colonne desiderate dal database, rivelando il nome utente e l'hash della password dell'utente amministratore.

La mancanza di controlli adeguati è introdotta in modules/Reports/ReportRun.php (righe 394-398). Ciascuno dei nomi di colonna forniti viene suddiviso su “:”.
$selectedfields = explode(":", $fieldcolname);
Se poi l'utente non è un amministratore, lo script verifica se il campo si trova in un array di campi consentiti generato dal modulo primario selezionato per il report:
!in_array($selectedfields[3], $permitted_fields[$module])
Tuttavia, si ricordi l'input fornito:
vtiger_users:user_name:Contacts_Salutation:salutationtype
Poiché i “campi consentiti” vengono confrontati con l'elemento all'indice 3 dell'array, il campo controllato è salutationtype nel modulo Contacts, che non è considerato sensibile e pertanto è consentito per l'esportazione. Tuttavia, la tabella e la colonna fornite nei primi due elementi dell'array non sono soggette a tale verifica, portando all'esposizione dei dati.
Il problema è stato risolto in questo commit modificando la validazione dei campi selezionati, in modo che vengano verificati rispetto ai campi consentiti hard-coded in ciascun modulo.
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;
}