La vulnérabilité peut être exploitée lorsque la fonction getItemForItemtype()
est appelée avec un paramètre contrôlable par l'utilisateur (authentifié sur GLPI ou non). Par exemple, dans le fichier ajax/common.tabs.php
ligne 63 :
if ($item = getItemForItemtype($_POST['itemtype'])) { if ($item instanceof CommonDBTM && $item->isNewItem() && (!isset($_POST["id"]) || !$item->can($_POST["id"],'r'))) { exit(); } }
La fonction getItemForItemtype()
essaie tout simplement d'instancier un objet avec le nom passé en paramètre (inc/db.functions.php
ligne 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; }
Le problème est que la fonction class_exists
vérifie si la classe existe en appelant les fonctions d'autoload enregistrées. C'est ici qu'intervient le fichier 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); } }
Si le nom de la classe commence par SimplePie
, alors tous les _
sont remplacés par des /
(ce qui permet de contourner les magic_quotes) et la fonction require_once
est appelée. Ainsi, il est possible d'inclure des fichiers locaux en appelant la page avec par exemple : itemtype=SimplePie_.._.._.._.._.._.._.._.._.._tmp_ponce
.
Plusieurs problèmes se posent pour exploiter correctement la vulnérabilité :
.
..php
et de contrôler le contenu de celui ciLe premier patch ajoute un check dans la fonction d'autoload de SimplePie sur le nom du fichier : 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;
On ne peut donc plus compter sur cette fonction d'autoload. Par contre, il est toujours possible de passer par une autre fonction, comme par exemple celle de Zend Framework. Celle ci se trouve dans le fichier lib/Zend/Loader/StandardAutoloader.php
, elle commence par vérifier si le nom de classe commence par Zend\
, puis construit le nom du fichier à inclure en fonction du nom de classe. De la même manière, itemtype=Zend\_.._.._.._.._.._.._tmp_ponce
permet d'inclure un fichier local.
Le check doit donc se faire avant tout appel à la fonction class_exists
dans getItemForItemtype
qui déclenche l'appel aux fonctions d'autoload.
Le patch complet est disponible ici : https://forge.indepnet.net/projects/glpi/repository/revisions/23200/diff/trunk/inc/autoload.function.php
Deux changements :
glpi_autoload
, si un .
est trouvé dans le paramètre alors la fonction die
est appeléeIl n'est donc plus possible d'inclure un fichier local se trouvant dans un autre dossier sans que le script ne s'arrête.