Wednesday, June 13, 2012

Comportement inattendu de LocalReAlloc sous XP SP3

Note: J'ai cherche (rapidement) si quelqu'un avait deja documente le comportement, mais je n'ai rien trouve. Si j'ai rate quelque chose, n'hesitez pas a m'envoyer les liens, je les rajouterai.

Ce week-end, je suis tombe sur une vulnerabilite qui m'a ammene a m'interesser au comportement de LocalReAlloc, et donc RtlReAllocateHeap. Le probleme se resumait a passer un pointeur de pile a des fonctions attendant un pointeur de tas. Vu que cela ne sonne pas tres bien en Francais, voici un morceau code illustrant cela:


Les appels a LocalFree ou LocalReAlloc dependent des branches et boucles internes a la fonction, et les tailles et index ont ete choisis par hasard.

Qui dit XP SP3, dit aussi cookie de tas, et verification des listes doublement chainees. Il ne sera donc pas surprenant de voir que LocalFree (RtlFreeHeap) va echouer la plupart du temps, sauf si vous etes tres doues. Voici un example des verifications qui peuvent vous attendre:


Au final, LocalFree rate, ne retourne pas NULL, et le pointeur passe n'est pas touche. Pour obtenir quelque chose d'interessant avec LocalFree, il faudrait passer la verification du cookie, et declencher une liberation vers le Lookaside par exemple.

Par contre, LocalReAlloc ne fonctionne pas pareil. D'apres le MSDN, si la reallocation rate, la fonction est sensee retourner NULL, avec un code d'erreur recuperable via GetLastError(). Mais en analysant la fonction, on se rend rapidement compte qu'il existe des chemins qui retournent directement le pointeur passe en cas d'erreur, sans meme verifier le cookie.

Par example, si l'entete du chunk n'est pas marque comme etant occupe (ou du moins ce que NtDll pense etre l'entete du chunk), une erreur STATUS_INVALID_PARAMETER va etre levee, mais la fonction va quand meme retourner le pointeur passe (en version simplifiee).

Voici les quelques blocs en question:

...
...

Un rapide programme pour tester cela donne (a lancer sans que le tas soit en mode debug):


LocalReAlloc retourne un pointeur non NULL, le programme pense que la reallocation a ete un succes. Alors que q pointe sur la pile et toute copie vers q va deborder l'espace reserve pour p au dela de 8 octets.
En lancant le meme programme sous Windows 7, une erreur critique est detectee, et le programme termine.

En soi, cela ne constitue pas une vulnerabilite. Par contre, couple a une corruption de pointeur comme expliquee au debut (ou meme LSB d'un pointeur de tas), on se retrouve avec un programme pensant avoir un espace fraichement realloue sur le tas, alors qu'il va ecraser des donnees quelque part ailleurs (pile dans notre exemple).