dimanche 10 juillet 2011, 21:00:02 (UTC+0200)

Audit partiel des composants Open Source intégrés à VMware ESX (SFCB)

Suite à un pari (que j'ai depuis perdu) avec Marc Olanié, je devais trouver une exécution de code à distance dans les produits ESX/vSphere de VMware, simplement en lisant le code de projets Open Source embarqués dans ces produits. Cet article a pour objectif de présenter les vulnérabilités identifiées lors de cette analyse et leur impact (ou pas) sur ESX/vSphere. De plus, la publication des détails techniques permettra d'identifier d'autres produits incluant des versions vulnérables de ces composants ou souffrant de bugs similaires. La première partie, ici présente, couvre le serveur CIMOM, alors que la deuxième (à venir) abordera SLP.


WBEM est une suite de protocoles veillant à fournir aux administrateurs d'informatique distribuée (dite Cloud) des outils centralisés de gestion, suivi et déploiement. SBLIM est l'implémentation de référence, sous Linux, de ces protocoles. La partie "serveur CIMOM" (pour Common Information Model Object Manager) est implémentée sous le nom de SFCB.


sfcbd (version testée : 1.3.6) écoute sur les ports TCP/5988 (HTTP) ou TCP/5989 (HTTPS) et accepte les requêtes POST et M-POST. Ce service étant avant tout un serveur Web "maison", la première chose à tester (en dehors du GET de 4100 caractères ;-) est la gestion de l'en-tête Content-Length, vu le nombre de vulnérabilités liées à ce traitement (Opera, Nagios, CA iGateway, Quicktime, Apache mod_proxy, Novell eDirectory, ...). Et on n'est pas déçu, l'oeil étant immédiatement attiré par un memcpy() (ligne 406) réalisé sur un tampon dont la taille est fournie par l'attaquant (ligne 405) :

401	static int getPayload(CommHndl conn_fd, Buffer * b)
402	{
403	   unsigned int c = b->length - b->ptr;
404	   int rc = 0;
405	   b->content = (char *) malloc(b->content_length + 8);
406	   if (c) memcpy(b->content, (b->data) + b->ptr, c);
407	
408	   if (c > b->content_length) {
409	     mlogf(M_INFO,M_SHOW,"--- HTTP Content-Length is lying; content truncated\n");
410	     c = b->content_length;
411	   }
412	
413	   rc = readData(conn_fd, (b->content) + c, b->content_length - c);
414	   *((b->content) + b->content_length) = 0;
415	   return rc;
416	}

Le critère de taille est bel et bien vérifié, mais *après* la copie, donc trop tard. Et hop, un heap overflow (CVE-2010-1937) ! Le correctif pour cette vulnérabilité est trivial, et consiste simplement à vérifier les données *avant* de les utiliser (cf. le diff coloré). Ce qui donne ce type de code :

401	static int getPayload(CommHndl conn_fd, Buffer * b)
402	{
403	   unsigned int c = b->length - b->ptr;
404	   int rc = 0;
405	
406	   if (c > b->content_length) {
407	     mlogf(M_INFO, M_SHOW,
408	           "--- HTTP Content-Length is lying; rejecting %d %d\n", c, b->content_length);
409	     return -1;
410	   }
411	
412	   b->content = (char *) malloc(b->content_length + 8);
413	   if (c) memcpy(b->content, (b->data) + b->ptr, c);
414	
415	   rc = readData(conn_fd, (b->content) + c, b->content_length - c);
416	   *((b->content) + b->content_length) = 0;
417	   return rc;
418	}

Pour autant, l'instruction malloc(b->content_length + 8) (ligne 412) est toujours vulnérable à un débordement d'entier débouchant sur un débordement de tampon si aucune vérification préalable n'a lieu. La fonction doHttpRequest() réalise en amont diverses vérifications sur la valeur du champ Content-Length, dont deux qui nous intéressent dans le cas présent :

841	         unsigned int maxLen;
842	         if (getControlUNum("httpMaxContentLength", &maxLen) != 0) {
843	           genError(conn_fd, &inBuf, 501, "Server misconfigured (httpMaxContentLength)", NULL);
844	           _SFCB_TRACE(1, ("--- exiting: bad config httpMaxContentLength"));
845	           commClose(conn_fd);
846	           exit(1);
847	         }
848	         if((clen >= UINT_MAX) || ((maxLen) && (clen > maxLen))) {
849	            genError(conn_fd, &inBuf, 413, "Request Entity Too Large", NULL);
850	            _SFCB_TRACE(1, ("--- exiting: content-length too big"));      
851	            commClose(conn_fd);
852	            exit(1);
853	         }
854	         inBuf.content_length = clen;

La variable clen correspond au b->content_length de getPayload(). Nous avons donc trois cas possibles, selon la valeur de l'option de configuration httpMaxContentLength :

  • elle n'est pas définie dans le fichier de configuration => une erreur est levée
  • elle est définie et vaut zéro => la variable clen doit être inférieure à UINT_MAX
  • elle est définie et est différente de zéro => la variable clen doit être inférieure à UINT_MAX et inférieure ou égale à httpMaxContentLength

Ce débordement d'entier est donc exploitable (CVE-2010-2054) si httpMaxContentLength vaut zéro ou est légèrement inférieure à UINT_MAX. Le correctif (cf. diff coloré) se contente d'interdire la valeur zéro :

842	         if ((getControlUNum("httpMaxContentLength", &maxLen) != 0) || (maxLen == 0)) {

La couverture n'est donc pas totale. Un administrateur mal intentionné pourrait par exemple positioner la variable à 4.294.967.290 (soit UINT_MAX - 5), et ainsi introduire un heap overflow exploitable à distance et sans authentification, sans pour autant impacter les aspects fonctionnels :-(


Je me sentais du coup bien parti pour gagner mon pari ! Sauf que les versions testées à l'époque (VMware ESXi 3.5, ESXi 4 et ESX 4) intégraient une version antérieure et modifiée de sfcbd (v1.3.3 sous ESX 4). CVE-2010-1937 y a été corrigé (silencieusement, c'est-à-dire sans informer ni ses clients ni le projet Open Source d'origine) et CVE-2010-2054 n'affecte pas les versions inférieures à 1.3.4.


Carrramba ! Encore raté ! Mais bon, la surface d'attaque d'un ESX côté réseau d'administration est vaste, et SLP fera l'objet de la deuxième partie de cette étude ...


Posted by Nicolas Grégoire | Permanent link | File under: vulnérabilités

webmaster@agarri.fr
Copyright 2010-2021 Agarri