Problématique Méthodes Existantes

publicité
Problématique
Internet Explorer est, depuis longtemps, une des cibles préférés des pirates dans le cadre de
l’écriture d’exploits. Tout simplement il s’agit du navigateur de référence sous Windows, qui
représente la très grande majorité du parc. Malheureusement, plus particulièrement depuis
Windows XP SP2, l’écriture d’exploits de type heap overflow devient de plus en plus
difficile, de par la mise en place d’un certain nombre de protections.
C’est bien souvent dans la heap que vont se produire la plupart des bugs qui peuvent mener à
une exploitation réussie.
Ce que l’on appelle communément la « heap » ou encore le « tas » est une zone de l’espace
d’adressage qui est réservé pour les allocations dynamiques faites pendant l’exécution du
programme au travers d’appels à un allocateur mémoire.
L’allocateur maintient donc plusieurs listes de blocs de mémoires, et, sans entrer dans les
détails, il faut savoir que la structure de chacun de ces blocs est la suivante :
Avant le SP2
La partie header permet à l’allocateur de garder, d’organiser et de gérer les différents blocs
mémoire. La partie donnée, elle, est la mémoire qui a été demandée par le programme.
Depuis Windows XP SP2, un nouveau champ est apparu dans le header, qui a pour but de
renforcer le contrôle d’intégrité de la heap, rendant l’exploitation plus difficile. De plus, il
effectue des vérifications supplémentaires quand à la cohérence des différents blocs entre eux.
Si jamais Windows détecte la corruption d’un des blocs, il va lancer une exception, mettant
fin à l’exécution du programme, réduisant à néant nos chances de réussir l’exploitation, et
donnant l’alerte.
Ces exploitations sont toujours possibles, mais demandent un contrôle de l’état de la heap. La
problématique se déplace donc vers le contrôle de l’état de la heap au moment de
l’exploitation, facteur crucial pour la réussite d’un exploit.
Méthodes Existantes
Actuellement, les méthodes efficaces d’exploitation de heap overflow se tournent de plus en
plus vers l’écrasement de données du programme lui-même que vers l’exploitation des
mécanismes internes de l’allocateur. Au vu des différentes protections, citées ci-dessus, qui
ont été mises en place, l’exploitation des mécanismes internes de l’allocateur devient en effet
plus compliqué que de tirer partie des données déjà présentes sur la heap et utilisées par le
programme ciblé, comme des pointeurs sur fonction ou des structures de données
particulières.
Pour que l’exploit réussisse, il faut que l’attaquant arrive à détourner le flux de l'exécution, de
sorte à exécuter son propre code. Pour ce faire, il va détourner le pointeur d'exécution (EIP)
pour l'amener à pointer vers des sections de données qu'il contrôle, plus précisément vers un
shellcode, qui est en fait du code exécutable qu’il aura injecté dans la mémoire du
programme.
Pour maximiser les chances de réussite, une technique assez répandue dénommée « heap
spraying » consiste à se répandre au maximum dans la heap du programme exploité, en tirant
partie de certaines instruction « inoffensives » (dites ‘NOP’) qui auront pour seul but de
maximiser nos chances
Précisons la difficulté de l’opération : si le pointeur d’exécution (EIP) pointe dans une zone
de données non contrôlée, il y a très fort à parier qu’il s’agisse soit d’instructions invalides ou
que le programme plante tout simplement en arrivant ici, car le code assembleur sera
incohérent. Par contre si notre pointeur d’exécution pointe dans la série de NOP, alors ces
instructions inoffensives seront exécutées et l’EIP finira par arriver à notre shellcode, qui lui
aura l’effet escompté. De même si notre pointeur d’exécution tombe au milieu de notre
shellcode, alors le programme crashera : il est nécessaire d’exécuter e shellcode depuis sa
première instruction.
Pour illustrer cela, considérons la situation suivante :
Nous avons ici une faible marge d’erreur de 10 instructions maximum pour sauter dans notre
shellcode : il existe dix adresses possibles pour le pointeur EIP afin que l’exploitation
réussisse. Le risque que le pointeur EIP soit dans une zone invalide (non contrôlée ou au
milieu du shellcode) est important. Mais imaginons plutôt la situation suivante, ou le nombre
d’instructions NOP est significativement augmenté:
Vous réalisez bien que la situation n’est plus la même, nous avons ici une marge d’erreur de
10 Mo (ce qui représente plusieurs millions d’instructions) pour atterrir dans notre la zone de
NOP afin d’exécuter le shellcode. Autant dire que les chances statistiques de réussir l’attaque
on très sérieusement augmenté.
Même si cette technique augmente considérablement les chances de réussite de l’exploit, elle
présente un certain nombre de limitations :
-
On ne peut prétendre remplir tout l’espace mémoire disponible, les chances de tomber
sur de la mémoire sous contrôle restent donc limitées.
Compromis chances de réussite / Mémoire consommée.
Consommation excessive de mémoire signifie perte de performances, ce qui peut
alerter la victime.
Vers un contrôle affiné de l’état de la mémoire
La méthode présentée par Alexander Sotirov à la black hat 2007 (à Amsterdam) propose de
tirer un trait sur ces limitations en affinant le contrôle de l’état de la mémoire, rendant donc
inutile ce remplissage excessif. Il propose d’utiliser les mécanismes internes du moteur
JavaScript de Internet Explorer pour prédire et contrôler l’état de la mémoire.
Le concept est le suivant :
<script language=’javascript’>
Var str1=’AAAAAAAAAAAAAAAAA’ ; // n’alloue pas de mémoire
Var str2 = str1.substr(0, 10) ; // alloue une chaîne de 10
caractères
</script>
Un des fondements de ce schéma d'attaque repose sur le fait que JavaScript, qui utilise
(normalement) une heap séparée, va en fait utiliser la heap de Internet Explorer lorsqu'il s'agit
d'allouer des chaînes de caractères.
En analysant plus profondément l’implémentation du moteur javascript on se rend compte
qu’il est même possible d’implémenter un équivalent des routines d’allocation et de libération
de mémoire en javascript. Ce qui veut dire qu’au lieu d’envahir la mémoire pour maximiser
nos chances, il est possible de prévoir très précisément ou nôtre bloc sera alloué.
En tenant compte du mode de fonctionnement du moteur JavaScript et d’un certain nombre de
facteurs comme :
- Le garbage collector de JavaScript
- Les problématiques de fragmentation dans le cadre de l’exploitation de heap overflow
- Les différentes listes maintenues par l’allocateur (lookaside buffers)
Alexander Sotirov en arrive à présenter une API tout à fait similaire à celle du système luimême, ce qui facilite considérablement la tâche. C’est un peu comme si l’attaquant pouvait
participer de manière intrusive au déroulement du programme.
Ce niveau de contrôle de l'état de la heap au travers d'un langage de si haut niveau est possible
car l'algorithme de l'allocateur est tel qu'il est facilement possible de créer une routine qui va
défragmenter la heap (donc remplir tous les trous qui auraient pu être crées au cours de
l'exécution du programme au travers de diverses allocations/libérations de mémoire), et de
mettre les listes internes maintenues par l'allocateur dans un état tel que l'on s'assure que
chaque nouvel appel à l'allocateur allouera de la mémoire 'fraîche' (on entend par la qu'il n'ira
pas chercher des blocs déjà utilises et libérés).
<script language=’javascript’>
Heap.alloc(0x2000) ;
Heap.alloc(0x2020, ‘to_be_freed’);
Heap.alloc(0x2020);
Heap.free(‘to_be_freed’);
</script>
Ce qui rends ce schéma d’attaque très réaliste est le fait que Internet Explorer laisse
JavaScript (donc un langage de script venant de l’extérieur en lequel on ne devrait pas avoir
confiance) impacter la heap du système lui-même. En partant de ce principe, il est possible,
d’augmenter très fortement les chances de réussite, puisque en connaissant l’adresse « de
base » de la heap il est possible de prédire très précisément l’adresse des blocs que nous
contrôlons, ce qui rends inutile la méthode de heap spraying.
De plus cette méthode est très probablement extensible à la plupart des navigateurs et des
moteurs JavaScript, ce qui va faciliter l’écriture d’exploits pour ce type de cibles.
Téléchargement