Bug parfait: utilisation de la confusion de types dans Flash. Partie 1

Bonjour encore! Demain, nous commencerons les cours dans un nouveau groupe sur le cours "Reverse Engineering" . Traditionnellement, nous partageons avec vous la traduction de documents utiles sur le sujet. C'est parti!

Il est important pour certains attaquants que l'exploit soit extrêmement fiable. Elle doit toujours conduire à l'exécution de code lorsqu'elle est lancée sur un système avec une plateforme et une version connues de Flash. Pour le créer, vous pouvez utiliser des bogues particulièrement de haute qualité. Cet article décrit l'utilisation de l'un de ces bogues, ainsi que les facteurs qui le rendent particulièrement adapté à un fonctionnement fiable.



Bug

CVE-2015-3077 - le problème de la confusion des types dans les paramètres du filtre Adobe Flash Button et MovieClip , qui vous permet de confondre tout type de filtre avec un autre. Je l'ai signalé début décembre 2015 et en mai, il a été corrigé. Le bogue se produit car le pirate peut écraser le constructeur utilisé pour initialiser l'objet filtre. Un exemple de code reproduisant le problème est présenté ci-dessous:



Ce code est quelque peu déroutant en raison de l'utilisation de l'opérateur [], qui est nécessaire pour la compilation dans Flash CS. Le code logiquement équivalent (qui n'est pas un fait qui compile) est donné ci-dessous:



Ce code définit le champ de filtre de l'objet: Button ou MovieClip sur BlurFilter, qui est ensuite stocké directement dans Flash. Le constructeur BlurFilter est ensuite remplacé par le constructeur ConvolutionFilter. Après cela, le getter est appelé et un objet ActionScript est créé pour stocker le BlurFilter d'origine. Cependant, le constructeur a déjà été remplacé, donc ConvolutionFilter est appelé. Cela produit un objet de type ConvolutionFilter, soutenu par le retour du BlueFilter d'origine.

En fin de compte, les champs ConvolutionFilter sont accessibles (lecture et écriture) comme s'ils appartenaient à BlurFilter. De même pour tout autre type de filtre. Cela ouvre un large éventail de manipulations utiles à l'exploitation.

Le diagramme ci-dessous montre l'emplacement des objets originaux en mémoire qui pourraient être confondus en utilisant cette vulnérabilité sous Linux 64 bits.



Dans deux cas, les pointeurs sont comparables aux entiers et aux nombres à virgule flottante qui peuvent être manipulés. Cela signifie que les pointeurs peuvent être lus et écrits directement. De plus, comme les champs des objets sont ordonnés et triés par taille selon la définition de la classe, ils sont toujours à des endroits prévisibles, pour que l'écriture et la lecture n'échouent pas. Ces propriétés sont importantes pour assurer la fiabilité de l'exploit.

Exploiter

Étant donné que l'exploitation de ce problème nécessite l'exécution répétée d'une confusion de types, j'ai commencé par créer une fonction utilitaire pour la confusion de types, FilterConfuse.confuse . Il remet également les choses en ordre: il ramène les constructeurs de filtres ActionScript à leur état normal pour appeler à plusieurs reprises une fonction vulnérable sans affecter le comportement d'ActionScript en dehors de la fonction elle-même.
La première étape a été de contourner ASLR en définissant l'adresse de la table de fonction virtuelle (brièvement vtable). Le moyen idéal pour cela est de confondre un objet avec une table virtuelle avec un objet dans lequel il y a un élément chevauchant la table virtuelle qui peut être manipulé. Mais la table de tous les objets de filtre a le même décalage. Au lieu de cela, j'ai utilisé l' objet BitmapData dans le DisplacementMapFilter pour déterminer l'adresse vtable.

Pour déterminer l'emplacement dans la mémoire BitmapData de l'objet, j'ai confondu DéplacementMapFilter avec BevelFilter. Cela a fait que le pointeur BitmapData stocké dans DéplacementMapFiltre s'aligne avec les propriétés de couleur BevelFilter ( shadowColor , shadowAlpha , highlightColor et highlightAlpha ). Ces propriétés sont prises en charge par deux entiers 32 bits (représentés comme scolor et hcolor au-dessus et en dessous), et les propriétés de couleur accèdent à 24 bits de chaque entier, tandis que les propriétés alpha accèdent aux 8 bits supérieurs. Si vous lisez ces propriétés et les combinez à l'aide de l'arithmétique des bits, vous pouvez extraire l'adresse BitmapData immédiate de l'objet.



Ensuite, vous devez lire la table virtuelle à partir du haut de l'objet BitmapData. Pour cela, j'ai utilisé la propriété matrix de l'objet ConvolutionFilter. Il est stocké sous la forme d'un pointeur vers un tableau de nombres à virgule flottante, sous lequel la mémoire est allouée lors de la définition de la propriété, et un tableau ActionScript contenant ces nombres est renvoyé lorsque la propriété est reçue. En définissant le pointeur de matrice sur un objet BitmapData, vous pouvez lire le contenu de cet objet à partir de la mémoire sous la forme d'un tableau de nombres à virgule flottante.

Pour définir le pointeur, j'ai confondu l'objet ConvolutionFilter avec l'objet DéplacementMapFilter (pas le même DéplacementMapFilter utilisé ci-dessus!) Et définir l'emplacement des BitmapData de l'objet ci-dessus dans la propriété mapPoint . La propriété mapPoint est un point avec les coordonnées entières x et y (p_x et p_y dans la figure ci-dessous) qui correspondent au pointeur de matrice dans ConvolutionFilter, ce qui a facilité la définition de cette valeur. Après cela, il est devenu possible de lire la vtable de l'objet BitmapData en utilisant un tableau matriciel de l'objet ConvolutionFilter (il convient de noter que pour cela, l'objet a dû être confondu avec DéplacementBitmapFilter, puis confondu avec ConvolutionFilter).



À ce stade, il devient plus difficile de maintenir la fiabilité de l'exploit en raison de l'utilisation de nombres à virgule flottante. Les valeurs vtable_low et vtable_high sont lues dans la matrice ConvolutionFilter sous forme de nombres à virgule flottante, car il s'agit d'un type de tableau. Mais, malheureusement, chaque valeur de pointeur valide n'est pas un nombre à virgule flottante valide. Cela signifie que la lecture de la valeur renverra NaN, ou pire, une valeur numérique pas entièrement correcte.

Idéalement, pour résoudre ce problème, vous devez accéder à vtable_low et vtable_high via un getter, qui les interprète comme des entiers, mais ce n'est pas parce que les éléments de filtre sont généralement flottants en raison de leur fonctionnalité.

Heureusement, la machine virtuelle AS2 est suffisamment paresseuse pour interpréter les nombres à virgule flottante - elle ne convertit une valeur en flottant que lorsqu'une opération est effectuée sur elle dans ActionScript. Les opérations originales ne nécessitent généralement pas d'interprétation, sauf pour des opérations spéciales comme l'arithmétique. Cela signifie que lors de la copie d'un nombre à virgule flottante d'un tableau matriciel vers vtable_low ou vtable_high, il conservera sa valeur en mémoire, même s'il n'est pas valide pour float, tandis que la variable dans laquelle il a été copié n'est pas utilisée dans ActionScript ou pour effectuer des opérations arithmétiques dans le natif code. Ainsi, si la valeur d'une variable est instantanément confondue avec un autre type qui prend en charge la plage complète de valeurs 32 bits, par exemple int, elle est garantie d'être la même que la valeur d'origine dans la mémoire du tableau matriciel. Par conséquent, afin d'éviter la non-fiabilité de l'exploit, il est important d'effectuer une confusion de type avant de manipuler les flottants dans ActionScript.

Pour ce faire, j'ai écrit une classe de conversion, FloatConverter , utilisant la confusion de types dans les filtres pour implémenter les fonctions entier-à-flottant et flottant-à-entier. Il confond la propriété ColorMatrixFilter de la matrice (ne la confondez pas avec la propriété de matrice ConvolutionFilter), qui est un ensemble de flottants intégrés, avec les propriétés de couleur et alpha GlowFilter qui accèdent à différents octets int.



De cette façon, vous pouvez implémenter une conversion fiable d'un float en un int, mais, malheureusement, cela ne fonctionne pas de manière fiable dans la direction opposée. Pour accéder au tableau de couleurs dans ColorMatrix dans ActionScript, l'ensemble du tableau est copié, même si vous n'accédez qu'au premier d'entre eux. Lors de la copie d'un tableau, chaque élément est converti en Number, qui inclut un appel aux pointeurs (par exemple, l'appel de la valeurOf d'un objet). Étant donné que le tableau de couleurs est le plus long de toute la classe GlowFilter, il est amassé lorsqu'il est confondu avec le GlowFilter. Cela signifie que la conversion de valeurs inconnues à partir de ce segment de mémoire peut se produire, ce qui entraînera un blocage si elles se réfèrent à des pointeurs non valides lors de la conversion en nombre. Par conséquent, pour int-to-float, j'ai implémenté un convertisseur flottant utilisant une autre confusion ConvolutionFilter et DisplacementMapFilter, qui est une conversion directe et n'appelle pas de valeurs inconnues à partir du tas.



Cela résout le problème des plantages causés par l'accès à des valeurs inconnues à partir du tas, mais, malheureusement, il existe un autre problème de fiabilité associé aux flottants dans cet exploit. Cela est dû à l'implémentation du getter de matrice ConvolutionFilter. Toutes les valeurs numériques dans ActionScript 2 sont de type Number, qui est l'union d'un entier et d'un pointeur en un nombre double. La matrice ConvolutionFilter d'origine est stockée sous la forme d'un tableau de nombres à virgule flottante, mais est copiée dans le tableau ActionScript pour préserver l'accès lorsque le getter de matrice est appelé et les valeurs sont converties pour doubler dans le processus. Ensuite, lors de l'appel du convertisseur flottant, ils sont reconvertis en nombres à virgule flottante.

La conversion d'un nombre à virgule flottante en un nombre à double précision et vice versa enregistre généralement sa valeur, mais pas si la valeur flottante est SNaN. Selon la spécification en virgule flottante, il existe deux types de NaN: le NaN silencieux (QNaN) et le NaN signal (SNaN). Lorsque QNaN apparaît, rien ne se passe, mais SNaN, dans certains cas, lève une exception à virgule flottante. En x86, la conversion du double en float entraîne toujours QNaN (même si le double provient de SNaN) pour éviter des exceptions inattendues.

Par conséquent, si les bits inférieurs du pointeur sont SNaN, il sera converti en QNaN, ce qui signifie que le premier bit (le premier bit de mantisse, bit 22) sera défini alors qu'il ne devrait pas. Ce problème peut être évité lors de la lecture de vtable - le troisième octet du pointeur contenant le premier bit peut être lu sans alignement pour confirmer la valeur actuelle. Ainsi, le code lira sans alignements (après avoir relu la table virtuelle avec le pointeur Bitmap augmenté de un) et ajustera la valeur int si le flotteur s'avère être SNaN.

En utilisant les convertisseurs flottants décrits ci-dessus, l'adresse vtable peut être convertie en un entier. Vous devez maintenant obtenir le code à exécuter à l'aide de cette adresse. Un moyen simple de déplacer le pointeur d'instruction consiste à remplacer la table virtuelle de l'objet (ou un pointeur sur un objet qui possède une table virtuelle). Cela peut être fait en confondant le tableau matriciel ConvolutionFilter et les BitmapData du pointeur DéplacementFilter.

La fin de la première partie. La deuxième partie de la traduction sera publiée un peu plus tard, et maintenant nous attendons vos commentaires et nous invitons tout le monde au cours de rétro-ingénierie OTUS.

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


All Articles