Image: UnsplashComme décrit dans un
article précédent sur
CVE-2019-0726 , la recherche parfois de détails sur une vulnérabilité déjà connue conduit à la découverte d'une nouvelle vulnérabilité. Et dans certains cas, il existe plusieurs de ces nouvelles vulnérabilités.
L'article a examiné deux fonctions de la bibliothèque dhcpcore.dll: le UpdateDomainSearchOption mentionné avec désinvolture et une analyse plus détaillée du DecodeDomainSearchListData qu'il appelle. Comme cela se produit toujours lors de la recherche de vulnérabilités, même si des conclusions importantes à la fin se résument à une ou deux fonctions, dans le processus d'analyse, vous devez examiner une quantité de code beaucoup plus importante. Et parfois, l'œil s'accroche à des bagatelles qui ne sont pas liées à la tâche en cours, mais peuvent avoir une signification indépendante ou être utiles plus tard. Supposons qu'à l'heure actuelle, il n'y ait pas de temps pour y prêter attention, mais de telles bagatelles sont reportées dans le sous-cortex et, si après un certain laps de temps il y a une opportunité de revenir vers eux et de vérifier leurs suppositions, ils surgissent à nouveau dans la conscience.
Et c'est arrivé cette fois. Lors de l'étude de la fonction DhcpExtractFullOptions, qui est responsable du traitement de toutes les options spécifiées dans la réponse DHCP du serveur, en particulier, en appelant UpdateDomainSearchOption, deux tableaux sur la pile de 256 éléments attirent immédiatement l'attention:

Dans ce cas, la présence de contrôles restreignant les valeurs des itérateurs de ces tableaux n'est pas perceptible. Puisqu'à ce moment nous analysions une autre vulnérabilité, ces informations n'étaient pas pertinentes. Il ne restait donc qu'à se souvenir de cette place dans le code pour y revenir plus tard.
Analyse
Quelques semaines s'écoulent et nous rappelons à nouveau la fonction DhcpExtractFullOptions qui avait attiré l'attention plus tôt. Nous nous tournons vers lui dans le désassembleur, peignons les morceaux de code précédemment incomplètement cadrés et essayons de comprendre quels sont les deux tableaux statiques qui nous intriguent.
Au tout début de l'exécution de la fonction, les tableaux et leurs itérateurs sont mis à zéro:

La fonction analyse toutes les options du paquet reçu du serveur DHCP, recueille ces informations puis les traite. De plus, selon les résultats de l'analyse, elle écrit également l'événement correspondant dans le service ETW (Event Tracing for Windows). C'est dans la journalisation des événements que participent les tampons qui nous intéressent. Avec beaucoup d'autres données, elles sont transférées vers la procédure EtwEventWriteTransfer. Le travail de préparation de toutes les données pour la journalisation est assez volumineux et n'a pas beaucoup d'importance pour la vulnérabilité que nous considérons, nous pouvons donc nous passer d'illustrations.
Il est plus important de déterminer comment ces tampons sont remplis. Le remplissage se produit dans la boucle d'analyse des options. Tout d'abord, une fonction avec le nom parlant ParseDhcpv4Option est appelée pour l'option actuelle reçue pour le traitement, qui remplit les champs de l'objet dhcp_pointers en fonction des données reçues, ou prend note d'une option inconnue lorsqu'elle rencontre un identificateur d'option avec une valeur pour laquelle il n'y a pas de gestionnaire.

Au retour de ParseDhcpv4Option, la valeur d'identifiant de l'option_tag actuelle est écrite dans l'élément suivant du tableau all_tags, le premier des tableaux qui nous intéresse. Si la fonction a rencontré une option inconnue et, par conséquent, n'a pas défini l'indicateur is_known_option, la valeur d'identificateur est également écrite dans l'élément suivant du deuxième tableau - unknown_tags. Naturellement, les noms significatifs des variables dans cet article ont déjà été obtenus en analysant le code.
Ainsi, le tableau all_tags stocke les balises de toutes les options du message reçu et le tableau unknown_tags ne stocke les balises que pour les options inconnues de l'analyseur. Dans le même temps, la vérification des valeurs des indices de ces tableaux est totalement absente. Par conséquent, les valeurs de ces indices peuvent dépasser 256 et conduire à l'écriture au-delà des limites allouées sur la pile pour les matrices de mémoire. Pour surcharger le premier tableau, il suffit d'envoyer un paquet avec un nombre d'options supérieur à 256 à partir du serveur DHCP. Il en va de même pour le deuxième tableau, la seule différence étant que les options que le client ne peut pas traiter doivent être envoyées.
Fonctionnement
Essayons maintenant d'utiliser un exemple pratique pour nous assurer que nos conclusions sont correctes. Pour commencer, nous prêtons attention au fait que les balises d'option occupent un octet, tandis que les éléments de tableau sont de type int, c'est-à-dire qu'ils sont de quatre octets. Ainsi, nous avons un débordement dans lequel nous contrôlons tous les quatre octets, et le reste est remis à zéro lors de l'écrasement.

Pour tester notre hypothèse, le moyen le plus simple est d'écraser le cookie de sécurité sur la fonction en question, ce qui déclenchera une exception liée au contrôle de sécurité. Simulons une situation dans laquelle le serveur DHCP envoie un nombre suffisant d'options de réécriture. Que ce soit des options 0x1a0 (416) avec l'identifiant 0xaa et une taille nulle. Ainsi, chaque option prend deux octets, et la taille totale des paquets avec tous les en-têtes est de 1100-1200 octets. Cette valeur se trouve dans le
MTU pour Ethernet, par conséquent, il y a des raisons de croire que le message ne sera pas fragmenté, ce qui nous permettra d'éviter d'éventuels effets négatifs.
Nous envoyons le paquet formé de la manière décrite en réponse à une demande du client DHCP et interceptons l'exception dans le processus svchost.exe correspondant sur la machine cliente:

Comme vous pouvez le voir sur la trace de la pile, le cookie de pile et l'adresse de retour de la fonction ont été réécrits par les identifiants des options de notre package.
Bien sûr, la création d'un exploit fonctionnel pour cette erreur nécessitera beaucoup d'efforts de la part de l'attaquant. Sur les systèmes modernes, le débordement de tampon sur la pile est une vulnérabilité assez complexe et exigeante en main-d'œuvre en raison de tous les mécanismes de défense existants. D'un autre côté, il ne faut pas oublier que tous ces mécanismes protègent contre l'écrasement de l'adresse de retour et des gestionnaires d'exceptions, ou interdisent l'exécution de code dans des régions de mémoire non prévues à cet effet, ou interfèrent avec la prédiction d'adresse. Par exemple, ils ne peuvent en aucun cas empêcher d'écraser ceux stockés sur la pile entre le tampon débordé et l'adresse de retour des variables locales. Et la fonction DhcpExtractFullOptions en question contient plusieurs variables potentiellement dangereuses dans cet intervalle.
Encore une fois, nous écrivons à Microsoft sur l'erreur détectée. Après une courte correspondance et une analyse de l'application, qui ont pris environ une semaine, nous obtenons la réponse qu'un identifiant CVE est en cours de préparation pour la vulnérabilité décrite, un correctif est prévu pour une publication en mars et des informations sur la vulnérabilité sont déjà disponibles chez Microsoft, a été signalée par quelqu'un plus tôt. Le fait, d'une manière générale, n'est pas surprenant, car l'erreur se trouve littéralement à la surface, et les tampons qui ne contiennent pas de vérification des limites des indices attirent toujours l'attention en premier et peuvent souvent être détectés par des outils d'analyse automatiques.
En mars, comme annoncé, une mise à jour a été publiée pour corriger l'erreur décrite, qui a reçu l'identifiant
CVE-2019-0697 . Le chercheur qui a rapporté les informations plus tôt était Mitch Adair, le même employé de Microsoft qui a découvert la vulnérabilité DHCP
CVE-2019-0547 corrigée en janvier.
Publié par Mikhail Tsvetkov, spécialiste en analyse d'application des technologies positives.