OW-002-netscape-jpeg, révision 1 25 Juillet 2000 (Note du traducteur : traduction achevée le 07 mai 2001) Vulnérabilité dans le traitement du marqueur COM JPEG dans les butineurs Netscape---------------------------------------------------------------- Cet avis explique une vulnérabilité dans les butineurs Netscape présente au moins depuis la version 3.0 et jusqu'à Netscape 4.73 et Mozilla M15. La vulnérabilité est fixée dans Netscape 4.74 et Mozilla M16. Impact ------ Il peut être possible, bien que difficile à faire fiablement dans une attaque du monde réel, pour un site web malveillant d'exécuter du code assembleur arbitraire dans le contexte du butineur web. Dans le cas de Netscape Mail ou News, l'attaque peut être effectuée via un message mail ou un article news, également. Détails de la vulnérabilité --------------------------- Le format de flux JPEG interchange consiste en une collection ordonnée de marqueurs, de paramètres, et de segments de données codées sur l'entropie. Beaucoup de marqueurs démarrent des segments de marqueurs, qui consistent en le marqueur suivi par une séquence de paramètres en relation. Les segments de marqueurs sont d'une longueur variable, avec le premier paramètre étant la longueur sur deux octets. Les longueurs codées incluent la taille du paramètre lui-même. Ainsi, les longueurs plus petites que 2 sont toujours invalides. Les butineurs Netscape utilisent la bibliothèque de décodage de l'Independent JPEG Group pour les fichiers au format JPEG File Interchange Format (JFIF). Toutefois, ils installent un gestionnaire sur mesure pour traiter le marqueur COM (commentaire) qui enregistre le commentaire en mémoire plutôt que de le sauter comme la bibliothèque le ferait. Malheureusement, le nouveau gestionnaire ne vérifie pas si la longueur du champ est valide, et soustrait 2 de la longueur encodée pour calculer la longueur du commentaire lui-même. Il alloue alors de la mémoire pour le commentaire (avec un octets supplémentaire pour sa terminaison en NUL) et rentre dans une boucle pour lire le commentaire vers cette mémoire. En fixant la longueur du champ à 1, il est possible de s'assurer que l'appel d'allocation mémoire (de 0 octet) va réussir. Comme la longueur calculée du commentaire est déclarée non signée, elle sera une grande valeur positive plutôt qu'une petite négative, donc la boucle ne se terminera pas avant la fin du flux JPEG. Elle lira le flux JPEG vers le tas, en réécrivant d'autres tampons de Netscape dynamiquement alloués, aussi bien que sur les structures internes de la mise en oeuvre de malloc(3). Exploiter cette vulnérabilité en exécutant du code arbitraire n'est pas trivial, mais possible sur quelques plate-formes. Le problème réel ---------------- Est-ce que le problème est le manque de vérification d'erreur dans le code ? Bien sûr. Une faute de programmeur. Est-ce un problème causé par le choix d'un langage de programmation qui n'offre pas de vérification de débordement et de compilateurs qui traditionnellement n'offrent pas de vérification de limite ? Partiellement. Toutefois, regardons combien de formats de fichiers différents, de langages, et de protocoles un butineur web moderne doit supporter. Tous les analyseurs de fichiers ont ils été initialement mis en oeuvre avec l'intention d'être robustes contre des données non de confiance et peut-être malveillantes ? Si non, tous les cas ont ils été couverts avec des vérifications supplémentaires maintenant ? Avons nous une raison de croire qu'il n'y a pas de bogue dans la mise en oeuvre de chacun de ceux-ci, ou avons nous des raisons de suspecter le contraire ? Des solutions ? Il y a peu qu'un utilisateur final puisse faire, mais vous pouvez prendre cet avis comme encore un autre rappel d'exécuter le navigateur web que vous utilisez pour accéder à des contenus de non confiance dans un environnement restreint, sans accès à vos données les plus critiques. Malheureusement, ceci n'est pas facile à faire dans le monde Win32, encore. Réparations ----------- Mettez à jour vers Netscape 4.74 ou Mozilla M16, ou plus récent. Comme alternative, les utilisateurs de Mozilla peuvent appliquer le patch suivant (contre Milestone 15) et recompiler les sources : --- mozilla/modules/libimg/jpgcom/jpeg.cpp.orig Tue Mar 28 02:08:15 2000 +++ mozilla/modules/libimg/jpgcom/jpeg.cpp Wed May 24 17:24:03 2000 @@ -469,6 +469,10 @@ /* Get 16-bit comment length word. */ INPUT_2BYTES(cinfo, length, return FALSE); + if (length < 2) { + cinfo->err->msg_code = JERR_BAD_LENGTH; + il_error_exit((j_common_ptr)cinfo); + } length -= 2; /* discount the length word itself */ PR_FREEIF(ic->comment); Contournement ------------- Inclus dans l'archive qui accompagne cet avis (voir ci-dessous) un patch binaire non officiel pour les versions plus anciennes des butineurs Netscape sur quelques plate-formes. Le code source et un binaire Win32 du programme patch sont fournis. Vous ne devriez seulement utiliser ce patch que si vous ne pouvez pas mettre à jour vers une version réparée. Il n'y a aucune garantie. Ce patch empêche le butineur d'installer son propre gestionnaire de marqueur COM plutôt que de réparer le gestionnaire lui-même. Ce dernier nécessiterait plus de code et résulterait dans un motif de recherche bien plus grand qui ne s'appliquerait pas à autant de versions de Netscape. Merci de noter que celui-ci peut refuser de s'appliquer à votre version de Netscape ou que celui-ci peut ne pas fonctionner pour vous. Assurez vous de vérifier que le patch a fonctionné comme destiné en essayant d'afficher le fichier de démonstration JFIF (crash.jpg). Votre butineur ne devrait plus planter, et vous devriez voir une image identique à celle dans valid.jpg (même si crash.jpg est en fait un fichier JFIF invalide, mais ce n'est plus un problème de sécurité et c'est juste la façon dont la bibliothèque de l'Independent JPEG Group fonctionne). Exploiter la vulnérabilité -------------------------- La vulnérabilité nous permet de réécrire des endroits du tas au delà de l'espace alloué. Nous sommes limités par les caractères imprimables, NUL et LF. Ainsi, la possibilité d'exploiter ceci pour faire plus qu'un plantage dépendra des configurations locales sur quelques plate-formes. D'abord nous avons besoin de décider sur quoi nous réécrivons. Les structures internes de la mise en oeuvre de la mémoire dynamique est la cible la plus prometteuse : elles sont toujours là bas et elles contiennent typiquement des pointeurs. Pour l'exemple ci-dessous, nous assumerons le malloc de Doug Lea (qui est utilisé par la plupart des systèmes Linux, libc 5 et glibc) et un locale pour un jeu de caractères 8 bits (tels que la plupart des locales qui viennent avec glibc, en incluant en_US, ou ru_RU.KOI8-R). Les champs suivants sont gardés pour tout les morceaux libres sur la liste : taille du morceau précédent (si libre), la taille de ce morceau, et des pointeurs vers les morceaux suivants et précédents. De plus, le bit 0 de la taille d'un morceau est utilisé pour indiquer si le morceau précédent est utilisé (LSB du morceau actuel est toujours zéro à cause le la taille de la structure et de l'alignement). En jouant avec ces champs avec attention, il est possible de tromper des appels à free(3) en réécrivant des endroits mémoire arbitraires avec nos données. free(3) vérifie si un morceau adjacent à celui étant libéré est utilisé et, si non, consolide les deux morceaux en déliant les morceaux adjacents de la liste. Délier un morceau implique de configurer le pointeur "next" du morceau précédent et le pointeur "previous" du morceau suivant où les deux de ces morceaux sont adressés via les pointeurs du morceau étant délié. Ainsi, afin d'obtenir le contrôle sur ces écritures de mémoire, nous avons besoin de réécrire les deux pointeurs dans un morceau (ou peut être d'allouer de la mémoire à ce moment) et de façon préférée réinitialiser le drapeau PREV_INUSE du morceau suivant. Ceci prend 13 octets sur un 32-bit little endian, tel que Linux/x86 (8 octets pour les deux pointeurs, 4 octets de réservation pour le champ taille précédent, et 1 octet pour réinitialiser le drapeau). En pratique, nous voudrons répéter le motif désiré de 16 caractères (duquel seulement 9 octets importent) au moins plusieurs fois pour augmenter nos chances en cas de morceaux alloués plus grands. Les pointeurs écrasés servent à stocker à la fois les adresses et les données, ce qui limite notre choix de données : elles doivent également représenter des adresses valides, et la mémoire à cette adresse doit être en écriture. Maintenant nous avons besoin de décider quel pointeur nous voulons réécrire (il n'y a pas d'utilité à réécrire ce qui n'est pas un pointeur avec une adresse). Un bon candidat serait toute adresse de retour sur la pile. Ceci fonctionnerait, mais ne serait pas très fiable puisque l'endroit d'une adresse de retour dépend de la quantité de données dans la pile. Une meilleure cible serait un pointeur de fonction. Nous ne voulons pas deviner des endroits exacts dans la pile et nous ne pouvons pas aller dans les sections ELF sur x86 (BS n'est pas un caractère imprimable). Donc nous sommes effectivement limités aux pointeurs dans les bibliothèques partagées. Un pointeur intéressant que nous pouvons utiliser est __free_hook, ainsi le second appel à free(3) nous donnera le contrôle. Les hooks de débogage sont toujours compilés quand le code de Doug Lea fait partie de la GNU libc. Notre prochaine décision est à propos de l'endroit où nous voulons que le contrôle soit transféré. Nous préférerions certainement d'avoir notre "shellcode" dans le fichier JFIF lui même. Cependant, l'ensemble limité de caractères pourrait nous empêcher de passer des adresses du tas. Nous devons installer dans la pile et y placer notre code via une autre partie du butineur avant de déborder. Nous pouvons utiliser un tas de NOP ou équivalents pour éviter d'avoir à fournir un endroit exact de la pile. Un compilateur pour produire des fichiers JFIF mettant en oeuvre l'approche ci-dessus est inclus dans l'archive d'accompagnement. Merci de noter que ceci n'est par aucun moyen limité à Linux/x86. C'est juste qu'une plate-forme devait être choisie pour l'exemple. Ainsi, il est connu que ceci est exploitable au moins sur les installations Win32 d'une façon très similaire (via ntdll!RtlFreeHeap). Références ---------- Les programmes supplémentaires et les fichier JFIF d'exemple mentionnés dans cet avis sont fournis dans l'archive d'accompagnement, qui peut être téléchargée via l'un de ces liens : http://www.openwall.com/advisories/OW-002-netscape-jpeg-r1.tar.gz http://www.openwall.com/advisories/OW-002-1.zip MD5 (OW-002-netscape-jpeg-r1.tar.gz) = 05b9879474e6b8988cd3141760e07826 MD5 (OW-002-1.zip) = ea3a7febd3d8410382bcbf7463cf32a3 Le format d'échange JPEG est documenté dans le Standard International ISO 10918-1 et la Recommandation CCITT (maintenant ITU-T) T.81. La spécification JFIF et la bibliothèque IJG sont disponibles sur : ftp://ftp.uu.net/graphics/jpeg/ Crédits et informations de contact ---------------------------------- Cette vulnérabilité a été trouvée et l'avis écrit par Solar Designer . Je voudrais remercier Kevin Murray de Netscape Communications et beaucoup d'autres de la communauté Mozilla et de Netscape pour leurs supports dans la gestion de cette vulnérabilité. Les version mises à jour de cet avis et des autres avis Openwall seront disponibles sur : http://www.openwall.com/advisories/ Note du traducteur : cette traduction française (non officielle) a été réalisée par Denis Ducamp et est disponible sur : http://www.groar.org/~ducamp/english.html#sec-trad