Vulnerability: In GLPI before version 9.5.2, there is a SQL Injection in the API's search function. Not only is it possible to break the SQL syntax, but it is also possible to utilise a UNION SELECT query to reflect sensitive information such as the current database version, or database user. The most likely scenario for this vulnerability is with someone who has an API account to the system. The issue is patched in version 9.5.2. A proof-of-concept with technical details is available in the linked advisory.
Vulnerability ID: CVE-2020-15226
CWE: CWE-89
Severity score: 2.9
Project: https://github.com/glpi-project/glpi
Published: 2020-10-07
File: inc/api/api.class.php
Commit message: Merge pull request from GHSA-jwpv-7m4h-5gvc

* Prevent SQL injection through search API

* better solution
Commit author: Cédric Anne

Vulnerable version (before fix):
 protected function searchItems($itemtype, $params = []) {       if (isset($params['criteria']) && is_array($params['criteria'])) {           // use a recursive closure to check each nested criteria          $check_criteria = function($criteria) use (&$check_criteria, $soptions) {             foreach ($criteria as $criterion) {                // recursive call                if (isset($criterion['criteria'])) {                   return $check_criteria($criterion['criteria']); protected function searchItems($itemtype, $params = []) {                    && $soptions[$criterion['field']]['nosearch']) {                   return __("Forbidden field ID in search criteria");                }             }              return true;

Fixed version (after fix):
 protected function searchItems($itemtype, $params = []) {       if (isset($params['criteria']) && is_array($params['criteria'])) {           // use a recursive closure to check each nested criteria          $check_criteria = function(&$criteria) use (&$check_criteria, $soptions) {             foreach ($criteria as &$criterion) {                // recursive call                if (isset($criterion['criteria'])) {                   return $check_criteria($criterion['criteria']); protected function searchItems($itemtype, $params = []) {                    && $soptions[$criterion['field']]['nosearch']) {                   return __("Forbidden field ID in search criteria");                }                 // Escape value to prevent SQL injection                $criterion['value'] = Toolbox::addslashes_deep($criterion['value']);             }              return true;

Patch:
patch: @@ -1631,8 +1631,8 @@ protected function searchItems($itemtype, $params = []) {
       if (isset($params['criteria']) && is_array($params['criteria'])) {
 
          // use a recursive closure to check each nested criteria
-         $check_criteria = function($criteria) use (&$check_criteria, $soptions) {
-            foreach ($criteria as $criterion) {
+         $check_criteria = function(&$criteria) use (&$check_criteria, $soptions) {
+            foreach ($criteria as &$criterion) {
                // recursive call
                if (isset($criterion['criteria'])) {
                   return $check_criteria($criterion['criteria']);
@@ -1653,6 +1653,9 @@ protected function searchItems($itemtype, $params = []) {
                    && $soptions[$criterion['field']]['nosearch']) {
                   return __("Forbidden field ID in search criteria");
                }
+
+               // Escape value to prevent SQL injection
+               $criterion['value'] = Toolbox::addslashes_deep($criterion['value']);
             }
 
             return true;
path: inc/api/api.class.php