Débordement de tampon - dans le tas    Enregistrer au format PDF

Les deux types d’exploitation qui suivent (basés sur l’overflow dans les segments bss et heap) sont légèrement différents du stack-based overflow. Dans celui-ci, le but ultime est finalement d’écraser l’adresse de retour pour changer le flux d’éxécution du programme. Dans le cas que nous allons traiter, les cas de dépassement de mémoire dans le heap, il n’y a plus possibilité de déterminer l’éloignement de cette adresse de retour.


par S3cur3D

Les deux types d’exploitation qui suivent (basés sur l’overflow dans les segments bss et heap) sont légèrement différents du stack-based overflow. Dans celui-ci, le but ultime est finalement d’écraser l’adresse de retour pour changer le flux d’éxécution du programme. Dans le cas que nous allons traiter, les cas de dépassement de mémoire dans le heap, il n’y a plus possibilité de déterminer l’éloignement de cette adresse de retour. Par conséquent, les overflows dans le heap reposent sur les variables stockées après le buffer. Plus que jamais, il est nécessaire non seulement d’être inventif pour savoir comment exploiter ce type de faille, mais surtout, il faut avoir une vision claire du déroulement du programme et savoir analyser les répercussions que peuvent avoir le changement de certaines variables sur le reste du programme. Bien qu’il n’y ait pas d’exemples d’école comme il a pu y en avoir dans le cas des overflows sur pile, nous avons décidé de donner un exemple d’exploitation commun, à savoir le dépassement de mémoire sur le nom d’un fichier qui permet l’ouverture d’un compte root par l’écriture dans le fichier /etc/passwd.
Je note ici que les overflows dans le heap sont les plus courant et souvent les plus difficiles à exploiter : les erreurs d’utilisation de malloc()/free() ou new/delete en C++ sont les plus fréquents et leurs exploitations très spécifiques. Il est souvent possible d’en obtenir une exécution arbitraire à la manière de l’article précédent, mais cela demande une connaissance bien plus fine des mécanismes de réservation dans le heap utilisés.

Un exemple d’exploitation

Le programme qui suit est le début d’un robot IRC. La particularité qu’a ce robot est qu’il loggue (enregistre) tout ce qu’il reçoit dans le fichier /tmp/irc_logs. Pour que tout le monde puisse l’utiliser comme il l’entend, le programme est un SRP.
Voici le code :

  1. //irc-logger.c : Bot de log IRC
  2.  
  3. #include <stdio.h> //Nécessaire au FILE
  4.  
  5. #include <netdb.h> //Bloc Fonctions Sockets
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>
  8. #include <sys/types.h>
  9.    #include <sys/socket.h>
  10. #include <sys/ioctl.h>
  11. #define h_addr h_addr_list[0]
  12.  
  13. typedef enum { false, true } bool; //Definition du type booléen
  14.  
  15. int vrecv(int);  //Déclaration des prototypes des fonctions utilisées
  16. bool debloq_sock(int);
  17. bool cnect(int,char *,int);
  18. int recu(int,char *);
  19. void erreur(void);
  20.  
  21. int main() {
  22.  
  23.         int sock = socket(AF_INET, SOCK_STREAM,0); //Identifiant de la connexion
  24.         int bytesRecv; //Nombre de bytes reçues pendant un envoi du serveur distant
  25.  
  26.         char *messages = malloc(500),*bufferRecv = malloc(1024); //Buffers d'envoi et de réception
  27.  
  28.         FILE *log; //Identifiant du fichier
  29.        
  30.         char *serveur = malloc(30); //Nom du serveur
  31.         int port; //Port
  32.        
  33.         char *pseudo_bot = malloc(30); //Pseudo du bot
  34.        
  35.         char *fichier_log = malloc(15); //Nom du fichier de logs
  36.         strcpy(fichier_log,"/tmp/irc_logs");   
  37.        
  38.         printf("Veuillez entrer l'adresse du serveur :\t"); //Demande d'informations à l'utilisateur
  39.         scanf("%s",serveur);   
  40.         printf("Veuillez entrer le port du serveur :\t");
  41.         scanf("%d",&port);
  42.         printf("\nVeuillez entrer le pseudo du bot :\t");
  43.         scanf("%s",pseudo_bot);
  44.        
  45.         printf("\nConnexion à %s:%d... ",serveur,port);       
  46.        
  47.         if (cnect(sock,serveur,port) > 0) { //Si la connexion a réussi...
  48.        
  49.                         printf("Ouverture du fichier %s... ",fichier_log,serveur,pseudo_bot);   //Ouverture du fichier
  50.                         if ((log = fopen(fichier_log,"at")) == NULL) erreur(); 
  51.                         printf("Ok\n\n");      
  52.                        
  53.                         strcpy(messages,"NICK "); //Ecriture et envoi du message d'authentification au serveur
  54.                         strcat(messages,pseudo_bot);
  55.                         strcat(messages,"\r\nUSER ");
  56.                         strcat(messages,pseudo_bot);
  57.                         strcat(messages," . . :");
  58.                         strcat(messages,pseudo_bot);
  59.                         strcat(messages,"\r\n");                       
  60.                 printf("Envoi : %s\n",messages);  
  61.                         send(sock,messages,strlen(messages),0);
  62.                        
  63.                         while (1) //Reception de tous les messages jusqu'à déconnexion (Ping Timeout)
  64.                                 switch(recu(sock,bufferRecv)) { //A-t-on reçu quelque chose ?
  65.                                         case -2: //La connexion n'est pas active
  66.                                         case -1: break; //Non
  67.                                         case 0: printf("Fermeture de la connexion\n"); exit(0); break; //La connexion vient de se terminer
  68.                                    default: //Oui, on l'écrit (+ traitement des données)
  69.                                         fprintf(log,"%s\n",bufferRecv);
  70.                                 }      
  71.                                        
  72.         }
  73.         else erreur();
  74.        
  75.         free(bufferRecv); //On libère toute la mémoire qu'on a pu réserver
  76.         free(pseudo_bot);      
  77.         free(messages);
  78.         free(serveur);
  79.         free(fichier_log);     
  80.        
  81.         close(sock);    //On ferme la connexion
  82.        
  83.         fclose(log); //On ferme le fichier
  84.        
  85.         return 0;
  86. }
  87.  
  88. bool cnect(int sock,char *hote,int port) { //Fonction de connexion à un serveur et un port donné
  89.         if (sock > 0) {
  90.                 struct sockaddr_in clientService;
  91.       clientService.sin_family = AF_INET;
  92.       struct hostent *HostInfo;
  93.       if (HostInfo = gethostbyname(hote)) { //Résoud le nom d'hôte
  94.         struct in_addr **a;  
  95.          a=(struct in_addr **)HostInfo->h_addr_list;
  96.          if (*a) {          
  97.                 clientService.sin_addr.s_addr = inet_addr(inet_ntoa(**a));
  98.                 clientService.sin_port = htons( port );
  99.          }
  100.          else { close(sock); printf("Echec de résolution\n"); }
  101.       }
  102.       else { close(sock); printf("Echec de résolution\n"); }
  103.       if ( connect( sock,(struct sockaddr*) &clientService, sizeof(clientService) ) == -1) { //Connexion
  104.                         close(sock);           
  105.         return false;
  106.       }
  107.                 else    if (debloq_sock(sock)) return true;    
  108.                                 else {
  109.                                         printf("Erreur dans le débloquage du socket\n");
  110.                                         return false;
  111.                                 }
  112.         }
  113.         else printf