Sécurité DHCP dans Windows 10: exploration de la vulnérabilité critique CVE-2019-0726



Image: Pexels

Avec la publication des mises à jour de janvier pour Windows, la nouvelle de la vulnérabilité extrêmement dangereuse CVE-2019-0547 dans les clients DHCP a ému le public. La cote CVSS élevée et le fait que Microsoft n'a pas publié immédiatement une évaluation des performances, ce qui a rendu difficile pour les utilisateurs de décider d'une mise à jour urgente du système, ont attisé l'intérêt. Certaines publications suggèrent même que l'absence d'index peut être interprétée comme la preuve qu'un exploit fonctionnel apparaîtra dans un avenir proche.

Des solutions comme MaxPatrol 8 peuvent identifier les ordinateurs du réseau qui sont vulnérables à des attaques spécifiques. D'autres solutions, telles que PT NAD, détectent elles-mêmes de telles attaques. Pour rendre cela possible, il est nécessaire de décrire à la fois les règles de détection des vulnérabilités des produits et les règles de détection des attaques contre ces produits. À son tour, pour que cela devienne possible, il est nécessaire que chaque vulnérabilité individuelle connaisse le vecteur, la méthode et les conditions de son fonctionnement, c'est-à-dire littéralement tous les détails et les nuances associés à l'opération. Une compréhension beaucoup plus complète et approfondie est nécessaire que ce qui peut généralement être compilé à partir de descriptions sur des sites de fournisseurs ou dans CVE, tels que:

La vulnérabilité se manifeste parce que le système d'exploitation traite incorrectement les objets en mémoire.

Donc, pour ajouter aux produits de la société les règles de détection des attaques sur une vulnérabilité nouvellement créée dans DHCP, ainsi que les règles d'identification des appareils affectés, vous devez comprendre les détails. Dans le cas de vulnérabilités binaires, patch-diff est souvent utilisé pour obtenir un aperçu des erreurs sous-jacentes, c'est-à-dire une comparaison des modifications apportées au code binaire d'une application, d'une bibliothèque ou du noyau du système d'exploitation avec un correctif spécifique, une mise à jour qui corrige cette erreur. Mais la première étape est toujours la reconnaissance.

Remarque : Pour accéder directement à la description de la vulnérabilité, en contournant les concepts DHCP sous-jacents, vous pouvez ignorer les premières pages et accéder directement à la section "Fonction DecodeDomainSearchListData".

Reconnaissance


Nous nous tournons vers le moteur de recherche et voyons tous les détails de vulnérabilité actuellement connus. Cette fois, il y a un minimum de détails, et tous sont des processions gratuites des informations recueillies à partir de la publication originale sur le site Web de MSRC. Cette situation est assez courante pour les erreurs découvertes par Microsoft lors d'un audit interne.

Nous découvrons dans la publication que nous sommes confrontés à une vulnérabilité de corruption de mémoire, qui est contenue dans les systèmes client et serveur de Windows 10 version 1803 et apparaît au moment où un attaquant envoie des réponses spécialement conçues à un client DHCP. Après quelques jours à partir de ce moment sur la page, les indices de performance apparaîtront également:



Comme vous pouvez le voir, MSRC a noté «2 - Exploitation moins probable». Cela signifie qu'une erreur avec une probabilité élevée n'est pas opérationnelle du tout, ou que l'opération est lourde de telles difficultés, dont le dépassement nécessitera des coûts de main-d'œuvre trop élevés. Certes, Microsoft n'a pas tendance à sous-estimer ces estimations. Ceci est en partie influencé par le risque de perte de réputation et en partie par une certaine indépendance du centre de réponse au sein de l'entreprise. Par conséquent, supposez: puisque la menace d'exploitation est indiquée dans le rapport comme peu probable, elle l'est certainement. En fait, cela aurait pu compléter l'analyse, mais il ne serait pas superflu de revérifier et au moins de découvrir quelle était la vulnérabilité. En fin de compte, malgré toute la personnalité indéniable, les erreurs ont tendance à se répéter et à se manifester ailleurs.

À partir de la même page, nous téléchargeons le correctif (mise à jour de sécurité) fourni sous la forme d'une archive .msu, le décompressons et recherchons les fichiers probablement liés au traitement des réponses DHCP côté client. Récemment, il est devenu beaucoup plus difficile de le faire, car les mises à jour ont commencé à être livrées non pas sous la forme de packages distincts qui corrigent des erreurs spécifiques, mais sous la forme d'un package cumulatif unique comprenant toutes les corrections mensuelles. Cela augmentait considérablement l'excès de bruit, c'est-à-dire les changements non liés à notre tâche.

Parmi l'ensemble des fichiers, la recherche trouve plusieurs bibliothèques adaptées au filtre, que nous comparons avec leurs versions sur un système non corrigé. La bibliothèque dhcpcore.dll est la plus prometteuse. Dans ce cas, BinDiff produit des modifications minimales:



En fait, à part les modifications cosmétiques apportées à une seule fonction - DecodeDomainSearchListData. Si vous connaissez bien le protocole DHCP et ses options qui ne sont pas utilisées trop souvent, vous pouvez déjà supposer que cette fonction traite la liste. Sinon, passez à la deuxième étape - l'étude du protocole.

DHCP et ses options


DHCP ( RFC 2131 | wiki ) est un protocole extensible dont les capacités de réapprovisionnement sont fournies par le champ d'options. Chaque option est décrite par une balise unique (numéro, identifiant), la taille occupée par les données contenues dans l'option et les données elles-mêmes. Cette pratique est typique des protocoles réseau, et l'une des options «implantées» dans le protocole est l'option de recherche de domaine décrite dans la RFC 3397 . Il permet au serveur DHCP de définir les terminaisons de nom de domaine standard sur les clients, qui seront utilisées comme suffixes DNS pour la connexion configurée de cette manière.

Soit, par exemple, les terminaisons de noms suivantes ont été définies sur notre client:

.microsoft.com .wikipedia.org 



Ensuite, dans toute tentative de détermination de l'adresse par le nom de domaine, les requêtes DNS substitueront les suffixes de cette liste tour à tour jusqu'à ce qu'un mappage réussi soit trouvé. Par exemple, si l'utilisateur a entré ru dans la barre d'adresse du navigateur, des requêtes DNS seront générées d'abord pour ru.microsoft.com, puis pour ru.wikipedia.org:



En fait, les navigateurs modernes sont trop intelligents et répondent donc aux redirections vers le moteur de recherche vers des noms qui ne sont pas similaires au FQDN. Par conséquent, ci-dessous, nous joignons la conclusion des utilitaires moins gâtés:



Il peut sembler au lecteur qu'il s'agit de cette vulnérabilité, car la simple possibilité de remplacer les suffixes DNS par un serveur DHCP, avec lequel tout appareil du réseau peut s'identifier, constitue une menace pour les clients qui demandent des paramètres réseau via DHCP. . Mais non: comme il ressort du RFC, cela est considéré comme un comportement documenté tout à fait légitime. En fait, le serveur DHCP est intrinsèquement l'un de ces composants approuvés qui peuvent avoir un fort impact sur les appareils qui y accèdent.

Option de recherche de domaine


L'option de recherche de domaine est numérotée 0x77 (119). Comme toutes les options, il est codé avec une balise à un octet avec le numéro d'option. Comme la plupart des autres options, immédiatement après la balise se trouve une taille à un octet des données suivant la taille. Des instances d'options peuvent être présentes dans le message DHCP plusieurs fois. Dans ce cas, les données de toutes ces sections sont concaténées dans l'ordre dans lequel elles apparaissent dans le message.



Dans l'exemple présenté, tiré de la RFC 3397 , les données sont divisées en trois sections, chacune de 9 octets. Comme vous pouvez le voir sur l'image, les noms de sous-domaine dans le nom de domaine complet sont codés avec une longueur sur un octet du nom, immédiatement suivi par le nom lui-même. Le codage du nom de domaine complet se termine par un octet nul (c'est-à-dire un nom de sous-domaine de taille nulle).

De plus, l'option utilise la méthode la plus simple de compression des données, ou plutôt, simplement l'analyse des points. Au lieu de la taille du nom de domaine, le champ peut contenir la valeur 0xc0. Ensuite, l'octet suivant définit le décalage par rapport au début des données d'option, qui doivent être utilisées pour rechercher la fin du nom de domaine.

Ainsi, dans cet exemple, une liste de deux suffixes de domaine est codée:

 .eng.apple.com .marketing.apple.com 

Fonction DecodeDomainSearchListData


Ainsi, le numéro d'option DHCP 0x77 (119) permet au serveur de configurer les suffixes DNS sur les clients. Mais pas sur les machines avec des systèmes d'exploitation Windows. Les systèmes Microsoft ont traditionnellement ignoré cette option, donc historiquement, la fin des noms DNS, si nécessaire, a été reportée via les stratégies de groupe. Cela s'est poursuivi jusqu'à récemment, lorsque la prochaine version de Windows 10, version 1803, a ajouté le traitement pour l'option de recherche de domaine. A en juger par le nom de la fonction dans dhcpcore.dll, dans laquelle les modifications ont été apportées, c'est dans le gestionnaire ajouté que l'erreur en question se trouve.

Se rendre au travail. Nous peignons un peu le code et découvrons ce qui suit. La procédure DecodeDomainSearchListData, en totale conformité avec le nom, décode les données de l'option de recherche de domaine du message reçu du serveur. En entrée, il reçoit un tableau de données compressé de la manière décrite dans le paragraphe précédent, et en sortie, il génère une chaîne terminée par un zéro contenant une liste de terminaisons de nom de domaine, séparées par des virgules. Par exemple, cette fonction convertit les données de l'exemple ci-dessus en une chaîne:

  eng.apple.com,marketing.apple.com 

DecodeDomainSearchListData est appelé à partir de la procédure UpdateDomainSearchOption, qui définit la liste renvoyée sur la valeur "DhcpDomainSearchList" de la clé de registre:
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{INTERFACE_GUID}\
stocker les principaux paramètres d'une interface réseau spécifique.



La fonction DecodeDomainSearchListData remplit en deux passes. Lors de la première passe, il effectue toutes les actions sauf l'écriture dans le tampon de sortie. Ainsi, la première passe est consacrée au calcul de la taille de la mémoire requise pour accueillir les données renvoyées. Lors de la deuxième passe, la mémoire est déjà allouée pour ces données et la mémoire allouée est remplie. La fonction est plutôt petite, environ 250 instructions, et son travail principal consiste à traiter chacune des trois options possibles pour le caractère représenté dans le flux d'entrée: 1) 0x00, 2) 0xc0 ou 3) toutes les autres valeurs. Le correctif hypothétique pour l'erreur liée au DHCP se résume essentiellement à ajouter une vérification de la taille du tampon résultant au début de la deuxième passe. Si cette taille est nulle, la mémoire n'est pas allouée au tampon et la fonction termine immédiatement l'exécution et renvoie une erreur:



Il s'avère que la vulnérabilité se manifeste dans les cas où la taille du tampon cible est nulle. En même temps, au tout début de l'exécution, la fonction vérifie les données d'entrée, dont la taille ne peut être inférieure à deux octets. Par conséquent, pour le fonctionnement, il est nécessaire de sélectionner une option non vide de suffixes de domaine de telle manière que la taille du tampon de sortie soit nulle.

Fonctionnement


La première chose qui vient à l'esprit est que vous pouvez utiliser les points d'analyse décrits précédemment afin que les données d'entrée non vides génèrent une ligne de sortie vide:



Un serveur configuré pour envoyer une option avec un tel contenu dans la réponse entraînera en fait une violation d'accès sur les clients non mis à jour. Cela se produit pour la raison suivante. À chaque étape, lorsque la fonction analyse une partie du nom de domaine complet, elle le copie dans le tampon cible et place un point après celui-ci. Dans l'exemple tiré du RFC, les données seront copiées dans le tampon dans l'ordre suivant:

 1). eng. 2). eng.apple. 3). eng.apple.com. 

Ensuite, lorsque le domaine contient une taille de domaine nulle, la fonction remplace le caractère précédent du tampon de destination par une virgule:

 4). eng.apple.com, 

et continue l'analyse:

 5). eng.apple.com,marketing. 6). eng.apple.com,marketing.apple. 7). eng.apple.com,marketing.apple.com. 8). eng.apple.com,marketing.apple.com, 

À la fin de l'entrée, il ne reste plus qu'à remplacer la dernière virgule par un caractère zéro et vous obtenez une ligne prête pour l'écriture dans le registre:

 9). eng.apple.com,marketing.apple.com 

Que se passe-t-il lorsqu'un attaquant envoie un tampon formé de la manière décrite? Si vous regardez l'exemple, vous pouvez voir que la liste qu'il contient se compose d'un élément - une chaîne vide. Lors de la première passe, la fonction calcule la taille des données de sortie. Étant donné que les données ne contiennent pas un seul nom de domaine non nul, la taille est nulle.

Lors de la deuxième passe, un bloc de mémoire dynamique est alloué pour y placer des données et copier les données elles-mêmes. Mais la fonction d'analyse rencontre immédiatement un caractère nul, ce qui signifie la fin du nom de domaine, et donc, comme cela a été dit, remplace le caractère précédent d'un point par une virgule. Et ici, nous sommes confrontés à un problème. L'itérateur de tampon cible est à la position zéro. Il n'y a aucun caractère précédent. Le caractère précédent appartient à l'en-tête du bloc de mémoire dynamique. Et ce même caractère sera remplacé par 0x2c, c'est-à-dire par une virgule.

Cependant, cela ne se produit que sur les systèmes 32 bits. L'utilisation de unsigned int pour stocker la position actuelle de l'itérateur de tampon cible introduit des ajustements au traitement sur les systèmes x64. Prenons une plus grande attention au morceau de code responsable de l'écriture d'une virgule dans le tampon:



L'unité est soustraite de la position actuelle à l'aide du registre eax 32 bits, tandis que lors de l'adressage du tampon, le code accède au registre de rax 64 bits complet. Dans l'architecture AMD64, toute opération avec des registres 32 bits annule la partie supérieure du registre. Cela signifie que dans le registre rax, qui contenait auparavant zéro, après soustraction, pas la valeur –1, mais 0xffffffff sera stockée. Par conséquent, sur les systèmes 64 bits, la valeur 0x2c sera écrite dans l'adresse buf [0xffffffff], c'est-à-dire bien au-delà des limites de la mémoire allouée au tampon.

Les données obtenues sont en bon accord avec l'évaluation des performances de Microsoft, car pour exploiter cette vulnérabilité, un attaquant doit apprendre à effectuer une pulvérisation de segments à distance sur un client DHCP et en même temps avoir un contrôle suffisant sur l'allocation de mémoire dynamique pour enregistrer des valeurs prédéfinies, à savoir une virgule et zéro octet, produit dans l'adresse préparée et conduit à des conséquences négatives contrôlées. Sinon, l'écriture de données vers une adresse non vérifiée entraînera une interruption du processus svchost.exe, ainsi que de tous les services actuellement hébergés, et un nouveau redémarrage de ces services par le système d'exploitation. Un fait que les attaquants dans certaines conditions peuvent également utiliser à leur propre avantage.

Cela semble être tout ce qui peut être dit au sujet de l'erreur sous enquête. Seul le sentiment demeure que c'est loin d'être la fin. Comme si nous n'avions pas considéré toutes les options. Il doit y avoir quelque chose de plus qui est caché dans ces lignes.

CVE-2019-0726


Probablement comme ça. Si vous examinez attentivement le type de données à l'origine de l'erreur et que vous la comparez à la façon dont cette erreur se produit exactement, vous remarquerez que la liste des noms de domaine peut être modifiée de telle sorte que le tampon résultant sera de taille non nulle, mais une tentative d'écriture en dehors de celle-ci est il en sera de même. Pour ce faire, le premier élément de la liste doit être une chaîne vide et tous les autres peuvent contenir des terminaisons de domaine normales. Par exemple:



L'option présentée comprend deux éléments. Le premier suffixe de domaine est vide, il se termine immédiatement par un octet zéro. Le deuxième suffixe est .ru. La taille de ligne calculée en sortie sera égale à trois octets, ce qui permettra de s'affranchir du contrôle imposé par la mise à jour de janvier sur le vide du buffer cible. Dans le même temps, zéro au tout début des données forcera la fonction à écrire une virgule avec le caractère précédent dans la chaîne résultante, mais comme la position actuelle de l'itérateur dans la chaîne est zéro, comme dans le cas considéré ci-dessus, l'enregistrement se produira à nouveau en dehors du tampon alloué.

Maintenant, il est nécessaire de confirmer les résultats théoriques obtenus dans la pratique. Nous simulons une situation dans laquelle le serveur DHCP envoie un message avec l'option présentée en réponse à une demande du client, et interceptons immédiatement une exception lors de la tentative d'écriture d'une virgule à la position 0xffffffff allouée sous la ligne de tampon résultante:



Ici, le registre r8 contient un pointeur sur les options entrantes, rdi est l'adresse du tampon cible sélectionné et rax est la position dans ce tampon où le caractère doit être écrit. Nous avons obtenu de tels résultats sur un système complètement mis à jour (en janvier 2019).

Nous écrivons sur le problème découvert dans Microsoft et ... ils perdent la lettre. Oui, cela arrive parfois même avec des fournisseurs réputés. Aucun système n'est parfait, et dans ce cas, vous devez rechercher d'autres moyens de communication. Par conséquent, une semaine plus tard, sans même recevoir de réponse automatique pendant ce temps, nous contactons le gestionnaire directement via Twitter et selon les résultats de plusieurs jours d'analyse de l'application, nous constatons que les détails envoyés n'ont rien à voir avec CVE-2019-0547 et représentent une vulnérabilité indépendante pour laquelle nouvel identifiant CVE. Un mois plus tard, en mars, la correction correspondante sort et l'erreur reçoit le numéro CVE-2019-0726 .

C'est ainsi que vous pouvez parfois essayer de comprendre les détails de la vulnérabilité 1 jour pour découvrir accidentellement 0 jour simplement en faisant confiance à votre intuition.

Publié par Mikhail Tsvetkov, spécialiste en analyse d'application des technologies positives.

Source: https://habr.com/ru/post/fr448378/


All Articles