Outils pour utilisateurs

Outils du site


writeup:cve-2014-8360-en

GLPI - CVE-2014-8360

The vulnerability can be exploited when the function getItemForItemtype() is called with a user controllable parameter (authenticated or not). For example, in ajax/common.tabs.php line 63:

if ($item = getItemForItemtype($_POST['itemtype'])) {
   if ($item instanceof CommonDBTM
       && $item->isNewItem()
       && (!isset($_POST["id"]) || !$item->can($_POST["id"],'r'))) {
      exit();
   }
}

The function getItemForItemtype() tries to instantiate an object with the name given in parameter. It can be found in inc/db.functions.php line 200.

/**
 * Get new item objet for an itemtype
 *
 * @since version 0.83
 *
 * @param $itemtype   string   itemtype
 *
 * @return itemtype object or false if class does not exists
**/
function getItemForItemtype($itemtype) {
 
   if (class_exists($itemtype)) {
      return new $itemtype();
   }
   return false;
}

The fact is that the function class_exists checks if the class exists by calling registered autoload functions. Let's see one of those function: SimplePie_Autoloader::autoload (which is in inc/autoload.function.php).

// SimplePie autoloader
spl_autoload_register(array(new SimplePie_Autoloader(), 'autoload'));
 
/**
 * Autoloader class
 *
 * @since version 0.84
 *
 * @package SimplePie
**/
class SimplePie_Autoloader {
 
 
   /**
    * Constructor
   **/
   public function __construct() {
      $this->path = GLPI_SIMPLEPIE_PATH;
   }
 
 
   /**
    * Autoloader
    *
    * @param string $class The name of the class to attempt to load.
   **/
   public function autoload($class) {
 
      // Only load the class if it starts with "SimplePie"
      if (strpos($class, 'SimplePie') !== 0) {
         return;
      }
      $filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) .
                  '.php';
      require_once($filename);
   }
}

If the class name begins with SimplePie then all _ occurrences are remplaced with / and the function require_once is called. Thus, it is possible to include a local file by calling the page with, for example, itemtype=SimplePie_.._.._.._.._.._.._.._.._.._tmp_ponce.

However, there are some problems to exploit correctly this vulnerability:

  • Some versions of PHP (I don't know which ones exactly) are filtering the class name before autoload functions are called. Thus it is impossible to provide a name with a ..
  • We need a way to write a local file with the extension .php and control its content.

First Patch

The first patch adds a check in the autoload function of SimplePie: https://forge.indepnet.net/projects/glpi/repository/revisions/23191/diff/branches/0.84-bugfixes/inc/autoload.function.php

Index: autoload.function.php
===================================================================
--- autoload.function.php	(révision 23190)
+++ autoload.function.php	(révision 23191)
@@ -352,6 +352,11 @@
    **/
    public function autoload($class) {
 
+      // empty classname or non concerted plugin or classname containing dot (leaving GLPI main treee)
+      if (empty($class) || is_numeric($class) || (strpos($class, '.') !== false)) {
+         return false;
+      }
+      
       // Only load the class if it starts with "SimplePie"
       if (strpos($class, 'SimplePie') !== 0) {
          return;

We cannot use this autoload function anymore. But it is still possible to use another, such as Zend Framework. It is in lib/Zend/Loader/StandardAutoloader.php. It begins by checking if the class name begins with Zend\ and construct the filename. So itemtype=Zend\_.._.._.._.._.._.._tmp_ponce still works !

Second patch

Complete patch is available here: https://forge.indepnet.net/projects/glpi/repository/revisions/23200/diff/trunk/inc/autoload.function.php

Index: autoload.function.php
===================================================================
--- autoload.function.php	(révision 23199)
+++ autoload.function.php	(révision 23200)
@@ -258,9 +258,11 @@
 
    // empty classname or non concerted plugin or classname containing dot (leaving GLPI main treee)
    if (empty($classname) || is_numeric($classname) || (strpos($classname, '.') !== false)) {
-      return false;
+      die("Security die. trying to load an forbidden class name");
    }
 
+   
+   
    $dir = GLPI_ROOT . "/inc/";
    if ($plug = isPluginItemType($classname)) {
       $plugname = strtolower($plug['plugin']);
@@ -294,6 +296,14 @@
       if (preg_match('/^CAS_.*/', $classname)) {
          return false;
       }
+      // Do not try to load Zend using GLPI autoload
+      if (preg_match('/^Zend.*/', $classname)) {
+         return false;
+      }
+      // Do not try to load Simplepie using GLPI autoload
+      if (preg_match('/^SimplePie.*/', $classname)) {
+         return false;
+      }
 
       $item = strtolower($classname);
    }
@@ -312,6 +322,9 @@
    }
 }
 
+// Use spl autoload to allow stackable autoload.
+spl_autoload_register('glpi_autoload');
+
 require_once (GLPI_ZEND_PATH . '/Loader/StandardAutoloader.php');
 $option = array(Zend\Loader\StandardAutoloader::LOAD_NS => array('Zend' => GLPI_ZEND_PATH));
 $loader = new Zend\Loader\StandardAutoloader($option);
@@ -320,12 +333,9 @@
 // SimplePie autoloader
 spl_autoload_register(array(new SimplePie_Autoloader(), 'autoload'));
 
-// Use spl autoload to allow stackable autoload.
-spl_autoload_register('glpi_autoload');
 
 
 
-
 /**
  * Autoloader class
  *

Two changes:

  • in glpi_autoload, if a . is found into the parameter, then die is called
  • the registration order of autoload functions is modified, meaning that calling order is also modified …

Thus it is not possible anymore to provide a name with a . without stopping the script.

writeup/cve-2014-8360-en.txt · Dernière modification: 2014/12/28 17:08 par tlk