User Tools

Site Tools


hack:buffer_overflow_attack

buffer overflow attack

Description

Les erreurs de Buffer overflow sont caractérisées par le fait de remplacer des fragments de mémoire du processus, qui ne devraient jamais avoir été modifiés intentionnellement ou involontairement. Le remplacement des valeurs de l'IP (Instruction Pointer), BP (Base Pointer) et d'autres causes d'exception de registres, des fautes de segmentation et d'autres erreurs se produisent. D'habitude ces erreurs mettent fin à l'exécution de l'application d'une façon inattendue. Les erreurs de Buffer overflow se produisent quand on opère sur des buffers de type char.

Exemples

Exemple 1

#include <stdio.h>
   int main(int argc, char **argv)
   {
   char buf[8]; // buffer for eight characters
   gets(buf); // read from stdio (sensitive function!)
   printf("%s\n", buf); // print out data stored in buf
   return 0; // 0 as return value
   }

Cette application très simple lit la contribution standard d'une zone de caractères et le copie dans le buffer de type char. La taille de ce buffer est de huit caractères. Après cela, les contenus du buffer sont affichés et il y a sortie d'application.

Compilation de programme :

  rezos@spin ~/inzynieria $ gcc bo-simple.c -o bo-simple
  /tmp/ccECXQAX.o: In function `main':
  bo-simple.c:(.text+0x17): warning: the `gets' function is dangerous and
  should not be used.

À ce stade, même le compilateur suggère que la fonction gets() n'est pas sûre.

Exemple d 'utilisation :

  rezos@spin ~/inzynieria $ ./bo-simple // program start
  1234 // we eneter "1234" string from the keyboard
  1234 // program prints out the conent of the buffer
  rezos@spin ~/inzynieria $ ./bo-simple // start
  123456789012 // we eneter "123456789012"
  123456789012 // content of the buffer "buf" ?!?!
  Segmentation fault // information about memory segmenatation fault

Nous nous débrouillons (mal)heureusement pour exécuter l'opération défectueuse par le programme et provoquons une sortie anormale.

Analyse de problème :

Le programme appelle une fonction, qui opère sur le buffer de type char et ne vérifie jamais le débordement de taille de ce buffer. Par conséquent, il est possible, intentionnellement ou non, de conserver plus de données dans le buffer, ce qui provoquera une erreur. La question suivante survient : le buffer conserve seulement huit caractères, donc pourquoi la fonction printf() en affiche douze ? La réponse vient de l'organisation du processus de la mémoire. Quatre caractères qui ont débordé du buffer remplacent aussi la valeur conservée dans un des registres, qui était nécessaire pour le revenir à une fonction correcte. La continuité de mémoire s'est ensuivie dans le fait d'imprimer les données conservées dans cette zone de mémoire.

Exemple 2

#include <stdio.h>
   #include <string.h>
 
   void doit(void)
   {
           char buf[8];
 
           gets(buf);
           printf("%s\n", buf);
   }
 
   int main(void)
   {
           printf("So... The End...\n");
           doit();
           printf("or... maybe not?\n");
 
           return 0;
   }

Cet exemple ressemble au premier. En plus, avant et après la fonction doit(), nous avons deux appels à la fonction printf ().

Compilation:

  rezos@dojo-labs ~/owasp/buffer_overflow $ gcc example02.c -o example02
  -ggdb
  /tmp/cccbMjcN.o: In function `doit':
  /home/rezos/owasp/buffer_overflow/example02.c:8: warning: the `gets'
  function is dangerous and should not be used.

Exemple d'utilisation:

  rezos@dojo-labs ~/owasp/buffer_overflow $ ./example02
  So... The End...
  TEST                   // user data on input
  TEST                  // print out stored user data
  or... maybe not?

Le programme entre les deux printf () définis appelle l'affichage du contenu du buffer, qui est rempli des données entrées par l'utilisateur.

  rezos@dojo-labs ~/owasp/buffer_overflow $ ./example02
  So... The End...
  TEST123456789
  TEST123456789
  Segmentation fault

Parce que la taille du buffer a été définie (char buf[8]) et qu'il a été rempli avec treize caractères de type char, le buffer déborde. Si notre application binaire est en format ELF, donc nous sommes en mesure d'utiliser un programme objdump pour analiser cela et trouver les informations nécessaires pour exploiter l'erreur de buffer overflow. Ci-dessous est la sortie produite par objdump. De cette sortie nous sommes en mesure de trouver les adresses, où est appellé printf() (0x80483d6 et 0x80483e7).

  rezos@dojo-labs ~/owasp/buffer_overflow $ objdump -d ./example02
  080483be <main>:
   80483be:       8d 4c 24 04             lea    0x4(%esp),%ecx
   80483c2:       83 e4 f0                and    $0xfffffff0,%esp
   80483c5:       ff 71 fc                pushl  0xfffffffc(%ecx)
   80483c8:       55                      push   %ebp
   80483c9:       89 e5                   mov    %esp,%ebp
   80483cb:       51                      push   %ecx
   80483cc:       83 ec 04                sub    $0x4,%esp
   80483cf:       c7 04 24 bc 84 04 08    movl   $0x80484bc,(%esp)
   80483d6:       e8 f5 fe ff ff          call   80482d0 <puts@plt>
   80483db:       e8 c0 ff ff ff          call   80483a0 <doit>
   80483e0:       c7 04 24 cd 84 04 08    movl   $0x80484cd,(%esp)
   80483e7:       e8 e4 fe ff ff          call   80482d0 <puts@plt>
   80483ec:       b8 00 00 00 00          mov    $0x0,%eax
   80483f1:       83 c4 04                add    $0x4,%esp
   80483f4:       59                      pop    %ecx
   80483f5:       5d                      pop    %ebp
   80483f6:       8d 61 fc                lea    0xfffffffc(%ecx),%esp
   80483f9:       c3                      ret
   80483fa:       90                      nop
   80483fb:       90                      nop

Si le deuxième appel à printf() informe l'administrateur sur le logout de l'utilisateur (par ex. session fermée), alors nous pouvons essayer d'omettre cette étape et finir sans appeller printf().

rezos@dojo-labs ~/owasp/buffer_overflow $ perl -e 'print "A"x12
."\xf9\x83\x04\x08"' | ./example02
So... The End...
AAAAAAAAAAAAu*.
Segmentation fault

L'application a fini son exécution avec une faute de segmentation, mais le deuxième appel à printf() n'avait pas de place.

Quelques mots d'explication : perl -e 'print “A”x12 .“\xf9\x83\x04\x08”' - imprimera douze caractères “ A ” et ensuite quatre caractères, qui sont en fait l'adresse de l'instruction que nous voulons exécuter. Pourquoi douze ?

     8 // size of buf (char buf[8])
  +  4 // four additional bytes for overwriting stack frame pointer
  ----
    12

Analyse du problème :

La sortie est la même comme dans le premier exemple. Il n'y a aucun contrôle sur la taille du buffer copié dans celui précédemment déclaré. Dans cet exemple nous remplaçons le registre d'EIP avec l'adresse 0x080483f9, qui est en fait un appel ret dans la dernière phase de l'exécution de programme. Comment utiliser les erreurs de buffer overflow d'une façon différente ? Généralement, l'exploitation de ces erreurs peut impacter :

  • application DoS
  • la re-exécution de fonctions
  • l'exécution de code (si nous sommes en mesure d'injecter le shellcode, décrit dans le document séparé)

Comment les erreurs de buffer overflow errors sont faites? Ces sortes d'erreurs sont très faciles à faire. Pendant des années elles étaient le cauchemar des programmeurs. Le problème est lié aux fonctions natives de C, qui ne se soucient pas de vérifier la longueur appropriée du buffer. Ci-dessous est la liste de telles fonctions et, s'ils existent, leurs équivalents sûrs :

  • gets() → fgets() - read characters
  • strcpy() → strncpy() - copy content of the buffer
  • strcat() → strncat() - buffer concatenation
  • sprintf() → snprintf() - fill buffer with data of different types
  • (f)scanf() - read from STDIN
  • getwd() - return working directory
  • realpath() - return absolute (full) path

Utilisez des fonctions équivalentes sûres, qui vérifient la longueur des buffers, chaque fois que c'est possible. À savoir :

  1. gets() → fgets()
  2. strcpy() → strncpy()
  3. strcat() → strncat()
  4. sprintf() → snprintf()

Ces fonctions qui n'ont pas d'équivalents sûrs devraient être réécrites avec des vérifications sûres implémentées. Le temps passé sur ça profitera dans l'avenir. Souvenez-vous que vous devez le faire seulement une fois.

Utilisez des compilateurs, qui sont en mesure d'identifier des fonctions dangereuses, des erreurs logiques et de vérifer si la mémoire est remplacée quand et où elle ne devrait pas être.

hack/buffer_overflow_attack.txt · Last modified: 2022/04/07 07:55 by 127.0.0.1