
Authentifizierte SQL-Injection-Schwachstelle in VTiger Open Source CRM v7.5
Entdeckt von: Jacob Elliott
07/13/23
Im Reports-Modul von VTiger CRM v7.5.0 gibt es eine unzureichende Überprüfung der ausgewählten Felder für den Bericht, die gespeichert und später beim Ausführen des Berichts als Second-Order-SQL-Injection wieder eingeführt werden. Dadurch kann der Angreifer beliebige Felder aus der Datenbank auslesen, einschließlich Benutzerpasswort-Hashes, Webservice-API-Zugriffsschlüssel und andere sensible Daten.
Nach der Authentifizierung am CRM kann der Benutzer zum Reports-Modul navigieren und einen neuen Bericht erstellen.

Aufgrund der Art und Weise, wie die Tabellen verbunden werden, scheint es am besten zu funktionieren, ein Modul mit Datensätzen als primäres Modul auszuwählen. Ich wählte Contacts, das einen Datensatz enthielt.

Als nächstes kann der Benutzer beliebige legitime Felder aus dem primären Modul auswählen und den Berichterstellungsprozess fortsetzen.

Schließlich kann der Benutzer auf die Schaltfläche klicken, um den endgültigen Bericht zu speichern, während er die Verbindungen mit einem Proxy-Tool wie BurpSuite abfängt. Im Parameter selected_fields werden die zuvor ausgewählten Felder im folgenden Format an die Speicherfunktion übergeben:
sql_table:sql_column:label:field_name
An diesem Punkt kann der Benutzer die Werte von sql_table und sql_column auf beliebige Werte ändern, die er aus der Datenbank auslesen möchte. Für dieses POC verwendete ich:
vtiger_users:user_name:Contacts_Salutation:salutationtype
und
vtiger_users:user_password:Contacts_First_Name:firstname
Nach dem Weiterleiten der modifizierten Anfrage wird uns der endgültige Bericht präsentiert, der die gewünschten Spalten aus der Datenbank enthält und so den Benutzernamen und den Passwort-Hash des Admin-Benutzers preisgibt.

Die fehlende ordnungsgemäße Überprüfung wird in modules/Reports/ReportRun.php (Zeilen 394–398) eingeführt. Jeder der angegebenen Spaltennamen wird anhand des ':' aufgeteilt.
$selectedfields = explode(":", $fieldcolname);
Und wenn der Benutzer kein Admin ist, prüft das Skript, ob das Feld in einem Array von zulässigen Feldern enthalten ist, das aus dem ausgewählten primären Modul für den Bericht generiert wird:
!in_array($selectedfields[3], $permitted_fields[$module])
Beachten Sie jedoch die eingegebene Eingabe:
vtiger_users:user_name:Contacts_Salutation:salutationtype
Da die 'zulässigen Felder' gegen das Element an Index 3 im Array geprüft werden, ist das zu prüfende Feld salutationtype im Contacts-Modul, das nicht als sensibel gilt und daher für den Export zugelassen ist. Die Tabelle und Spalte in den ersten beiden Elementen des Arrays unterliegen jedoch keiner solchen Überprüfung, was zur Datenoffenlegung führt.
Dieses Problem wurde in diesem Commit behoben, indem die Validierung der ausgewählten Felder geändert wurde, sodass sie gegen die in jedem Modul fest codierten erlaubten Felder geprüft werden.
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;
}