Archive for février 12th, 2007

Heap Overflows Revisited, Part 1

Lorsque je m’ennuis, il m’arrive souvent de ressortir les vieilles choses. Me faisant alors chier comme un rat mort, je me suis intéressé à une chose qui me faisait un peu peur, les heap overflows sous windows, de nombreux papers ont déjà été écrit sur le sujet mais au lieu de reprendre leurs contenu, j’ai tenté de déterminer ou et comment leurs auteurs avaient trouvé tant d’infos sur le sujet.

Il faut savoir que la gestion du heap sous windows n’est pas documentée de façon officielle, Microsoft nous fournit quelques API pour créer un heap, l’allouer, le libérer et le détruire mais c’est tout, voyez plutôt :
http://msdn2.microsoft.com/en-us/library/aa366711.aspx

Cependant il est possible à l’aide d’outils comme les symboles de débuggage fournit par Microsoft, IDA et Ollydbg, de reverser certains méchanismes, attention ceci est à relativiser, vouloir reverser tout le fonctionnement du heap demande un travaille énorme que certains ont plus ou moins réalisé, dans le cas présent ceci n’est pas mon but (je suis f0u mais quand même :s).

Depuis l’arrivé du SP2 sur windows XP les routines de management du heap sont devenus plus sûres, exploiter un debordement dans le tas de façon classique est devenu impossible. J’ai donc regarder à quel niveau ce faisait ces vérifications, mais tout d’abord posons quelques définitions sur les structures utilisées pour gérer le tas.

Lorsqu’un bloc mémoire est alloué celui est précédé d’une structure appelé ‘chunk’:

typedef struct _HEAP_ENTRY
{
union
{
struct
{
USHORT Size;
USHORT PreviousSize;
};
PULONG SubSegmentCode;
};
UCHAR SmallTagIndex;
UCHAR Flags;
UCHAR UnusedBytes;
UCHAR SegmentIndex;
} HEAP_ENTRY, *PHEAP_ENTRY

On peut le voir sous olly :

La mémoire alloué est en 0x360640, et est précedée d'une structure, HEAP_ENTRY.
00360640                          06 00 08 00 F3 07 18 00          ..ó.
00360650  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

Un peu d’info sur les champs de cette structure, je reprend l’article de Kortchinsky paru dans Misc en modifiant 2, 3 choses.
Descriptif des champs :

- « Size » – USHORT, 2 octets : taille du chunk courant. Elle correspond à un nombre de cellules de 8 octets occupées par le chunk. Lors de la demande d’allocation, la taille demandée est arrondie à un multiple de 8 (supérieur ou égal), auquel s’ajoutent les 8 octets necessaires au stockage des champs du chunk. La taille stockée ici répond donc à l’opération (tailledemandee + 7) >> 3 + 1, soit 65 pour 512 octets, 18 pour 129, etc ;

- « PreviousSize » – USHORT, 2 octets : taille du chunk précédant, possède les mêmes caractéristiques que « Self size » ;
- « SmallTagIndex » – UCHAR, 1 octet : uniquement utilisé en mode debug (héhé pas que ca, on le verra plus tard).

- « Flags » – UCHAR, 1 octet : indicateurs des propriétés du chunk, chaque bit positionné à 1 de cet octet faisant état d’une caractéristique particulière du chunk, ils peuvent être trouvés sur le web (les plus utiles seront détaillés par la suite) :
0×01, HEAP_ENTRY_BUSY
0×02, HEAP_ENTRY_EXTRA_PRESENT
0×04, HEAP_ENTRY_FILL_PATTERN
0×08, HEAP_ENTRY_VIRTUAL_ALLOC
0×10, HEAP_ENTRY_LAST_ENTRY
0×20, HEAP_ENTRY_SETTABLE_FLAG1
0×40, HEAP_ENTRY_SETTABLE_FLAG2
0×80, HEAP_ENTRY_SETTABLE_FLAG3
- « UnusedBytes » – UCHAR, 1 octet : nombre d’octets non utilisés dans le buffer alloué, égal à la taille allouée à laquelle est soustraite la taille demandée – information d’une utilité très relative.

- « SegmentIndex » – UCHAR, 1 octet : indice du segment auquel appartient le chunk, en référence au tableau des segments du tas.

La structure d’un chunk libre est en fait une HEAP_ENTRY possédant comme dernier champ une LIST_ENTRY (double liste chaînée) sur les autres chunks libres. Cela donne sous Olly :

00360640                          37 01 08 00 F3 14 18 00          7.ó.
00360650  78 01 36 00 94 01 36 00 EE FE EE FE EE FE EE FE  x6.x6.îþîþîþîþ

On voit bien les 2 pointeurs Flink et Blink vallant 0×00360178 et 0×00360194, à noté que la structure ne change pas de position en mémoire, les 2 pointeurs sont ajouté à la suite comme on à libéré le Heap.

Lors de l’allocation la fonction HeapAlloc va chercher dans la liste des chunks libre de notre heap celui qui correspond à la taille demandée, le système va donc délinker un chunk pour l’allouer ensuite. C’est à ce niveau que une des vulnérabilités de la gestion du tas apparaît, en effet lors de l’opération de « unlink » le Flink (pointeur sur le chunk libre suivant) du chunk précédent doit être mis à jour pour pointer sur un chunk libre et vise versa. Le système utilise donc les pointeurs du futur chunk alloué pour faire cela.

L’opération correspond en C:

typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;

LIST_ENTRY Chunk; //LIST_ENTRY du chunk qui va être alloué
Chunk.Blink->Flink=Chunk.Flink; // PrevChunk vers le NextChunk
Chunk.Flink->Blink=Chunk.Blink; // NextChunk vers le PrevChunk

En ASM cela donne :

mov eax, Flink
mov ecx, Blink
mov [ecx], eax
mov [eax+4], ecx

Ainsi si par inadvertance le chunk avait été altéré il est possible de d’écrire ou l’on veut en mémoire en controlant les valeurs de Flink et de Blink, en théorie ;)

Mais vous comprendrez dans la suite que sous Windows il n’est pas si simple d’altérer ces chunks sans avoir des petits soucis ;)

Ivanlef0u

Add comment février 12th, 2007


Calendar

février 2007
L Ma Me J V S D
« jan   mar »
 1234
567891011
12131415161718
19202122232425
262728  

Posts by Month

Posts by Category