CVE-2021-34187 (published 2021-06-28T16:15Z)

main/inc/ajax/model.ajax.php in Chamilo through 1.11.14 allows SQL Injection via the searchField, filters, or filters2 parameter.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-89
cwe_name: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/chamilo/chamilo-lms
commit_details: repo_url: https://github.com/chamilo/chamilo-lms
author: Julio Montoya
author_date: 2021-05-28 09:09:43+02:00
msg: Fix database field from 1.11.x

https://github.com/chamilo/chamilo-lms/commit/f7f93579ed64765c2667910b9c24d031b0a00571
repository: repo_url: https://github.com/chamilo/chamilo-lms
repo_name: chamilo/chamilo-lms
description: Chamilo is a learning management system focused on ease of use and accessibility
repo_language: PHP
owner: chamilo
file_changes: filename: model.ajax.php
change_type: ModificationType.MODIFY
diff: @@ -109,6 +109,8 @@ function getWhereClause($col, $oper, $val)
         'nc' => 'NOT LIKE',  //doesn't contain
     ];
 
+    $col = Database::escapeField($col);
+
     if (empty($col)) {
         return '';
     }
@@ -1442,8 +1444,9 @@ switch ($action) {
         }
 
         $whereCondition = " AND $whereCondition ";
+        $columnOrderValidList = array_merge(['firstname', 'lastname'], $columns);
+        $sidx = in_array($sidx, $columnOrderValidList) ? $sidx : 'title';
 
-        $sidx = in_array($sidx, $columns) ? $sidx : 'title';
         $result = get_work_user_list(
             $start,
             $limit,
@@ -2505,18 +2508,11 @@ switch ($action) {
                 }
                 $result = $obj->getUserGroupNotInCourse(
                     $options,
-                    $groupFilter,
-                    false,
-                    true
+                    $groupFilter
                 );
                 break;
             case 'registered':
-                $result = $obj->getUserGroupInCourse(
-                    $options,
-                    $groupFilter,
-                    false,
-                    true
-                );
+                $result = $obj->getUserGroupInCourse($options, $groupFilter);
                 break;
         }
 

programming_language: PHP
filename: database.lib.php
change_type: ModificationType.MODIFY
diff: @@ -751,4 +751,9 @@ class Database
     {
         return self::getManager()->getConnection()->getSchemaManager()->listTableColumns($table);
     }
+
+    public static function escapeField($field)
+    {
+        return self::escape_string(preg_replace("/[^a-zA-Z0-9_]/", '', $field));
+    }
 }

programming_language: PHP
filename: extra_field.lib.php
change_type: ModificationType.MODIFY
diff: @@ -1584,7 +1584,7 @@ class ExtraField extends Model
                         if ($freezeElement) {
                             $form->freeze('extra_'.$variable);
                         }
-                        break;
+                        break;                        
                     case self::FIELD_TYPE_FILE:
                         $fieldVariable = "extra_{$variable}";
                         $fieldTexts = [
@@ -2608,6 +2608,7 @@ JAVASCRIPT;
                     }
                 } else {
                     // Extra fields
+                    $ruleField = Database::escapeField($rule->field);
                     if (false === strpos($rule->field, '_second')) {
                         //No _second
                         $original_field = str_replace($stringToSearch, '', $rule->field);
@@ -2630,7 +2631,7 @@ JAVASCRIPT;
                                 $conditionArray[] = ' ('
                                 .$this->get_where_clause($rule->field, $rule->op, $rule->data)
                                 .') ';
-                                $extraFields[] = ['field' => $rule->field, 'id' => $field_option['id']];
+                                $extraFields[] = ['field' => $ruleField, 'id' => $field_option['id']];
                             }
                                 break;
                             case self::FIELD_TYPE_TAG:
@@ -2639,10 +2640,11 @@ JAVASCRIPT;
                                     break;
                                 }
 
+                                    // Where will be injected in the parseConditions()
                                 //$where = $this->get_where_clause($rule->field, $rule->op, $rule->data, 'OR');
                                 //$conditionArray[] = " ( $where ) ";
                                 $extraFields[] = [
-                                'field' => $rule->field,
+                                        'field' => $ruleField,
                                 'id' => $field_option['id'],
                                 'data' => $rule->data,
                             ];
@@ -2656,7 +2658,7 @@ JAVASCRIPT;
                                     $where = $this->get_where_clause($rule->field, $rule->op, $rule->data, 'OR');
                                     $conditionArray[] = " ( $where ) ";
                                     $extraFields[] = [
-                                        'field' => $rule->field,
+                                        'field' => $ruleField,
                                         'id' => $field_option['id'],
                                         'data' => $rule->data,
                                     ];
@@ -2668,7 +2670,7 @@ JAVASCRIPT;
                         $original_field = str_replace($stringToSearch, '', $my_field);
                         $field_option = $this->get_handler_field_info_by_field_variable($original_field);
                         $extraFields[] = [
-                        'field' => $rule->field,
+                            'field' => $ruleField,
                         'id' => $field_option['id'],
                     ];
                     }
@@ -2689,6 +2691,8 @@ JAVASCRIPT;
      */
     public function get_where_clause($col, $oper, $val, $conditionBetweenOptions = 'OR')
     {
+        $col = Database::escapeField($col);
+
         if (empty($col)) {
             return '';
         }
@@ -2755,7 +2759,7 @@ JAVASCRIPT;
                             $inject_extra_fields .= " fvo$counter.display_text as {$extra['field']}, ";
                             break;
                         case self::FIELD_TYPE_TAG:
-                            //$inject_extra_fields .= " tag$counter.tag as {$extra['field']}, ";
+                            // If using OR
                             // If using AND
                             $newCounter = 1;
                             $fields = [];
@@ -3201,7 +3205,7 @@ JAVASCRIPT;
         );
 
         if (empty($defaultValueId)) {
-            $slct->addOption(get_lang('Please select an option'), '');
+            $slct->addOption(get_lang('Please select an option'));
         }
 
         foreach ($options as $value => $text) {

programming_language: PHP