Quand fonctionnalité ne rime pas avec sécurité

Wed 03 October 2007 by alex

Qui a dit que l'exploitation d'une faille noyau ou d'une faille à distance devait être compliquée ? Ce billet décrit une faille apparue pendant l'été 2006 avec l'ajout d'une nouvelle fonctionnalité : l'appel système prctl.

L'appel système prctl

L'appel système prctl permet de contrôler de multiples paramètres lors de l'exécution d'un processus : quel signal doit-il envoyer lorsqu'il meurt, peut-on le dumper, etc.

Le paramètre qui nous préoccupe est PR_SET_DUMPABLE. Un processus est dumpé par exemple quand il plante. Il reçoit alors le signal SIGSEGV, et le gestionnaire par défaut de ce signal indique qu'il doit créer une image mémoire du processus afin d'être débuggé pour corriger le problème : ce sont les fameux fichiers core (ou coredump).

Par défaut, une règle de sécurité sous Unix interdit de dumper un processus qui n’appartient au lanceur. C’est en particulier le cas des binaires Set-UID. Ces programmes tournent sous l’identité, non du lanceur, mais du propriétaire du fichier, root en général. Une règle de sécurité Unix interdit à un tel processus, lorsqu’il plante, de créer un fichier core, sauf dans des conditions bien précises. L’appel système prctl fournit une possibilité de gérer cela au cas par cas, processus par processus, soit en interdisant le dump pour ses propres processus, soit en autorisant le dump même pour les processus Set-UID. A priroi, au pire, tout ce que permet cet appel système est de dumper un processus qui ne pourrait normalement l’être, du moment qu’il est lancé par l’utilisateur.

Exploiter l’appel système prctl

Nous venons de voir que tout ce que permet prctl, c’est de dumper un processus, dans n’importe quel répertoire, même s’il est set-UID. Cela s’avère suffisant pour prendre le contrôle d’un système.

Le principe de l’exploitation est très simple. Il s’agit de créer un fichier core, mais de faire en sorte que :

  • il contienne les instructions de notre choix ;
  • il soit exécuté par un programme avec des privilèges supérieurs aux nôtres.

Pour ceux qui connaissent le fonctionnement d’un système Unix, la cible est toute désignée : le démon chargé d’exécuter des tâches à intervalle régulier, cron. Ce n’est pas le seul vecteur d’attaque envisageable. On pourra aussi utiliser logrotate. En fait, d’une manière générale, tout démon qui s’appuie sur des fichiers pour s’exécuter est une cible potentielle. En pratique, peu d’entre eux sont satisfaisants pour différentes raisons :

  • xinet et son répertoire de configuration par défaut. Mais d’une part, cela demande un redémarrage du système. D’autre part, la conformité de la syntaxe du fichier core par rapport aux attentes du démon est bloquante.
  • cron.hourly et autres demandent à ce que le fichier soit exécutable, ce qui n’est pas le cas.
  • d’autres répertoires dans /var sont aussi éliminés pour des raisons similaires.

L’exploitation consiste donc à créer un fichier qui sera pris en charge par cron, et qui contiendra donc une entrée avec les commandes de notre choix. Et pour créer ce fichier, on va provoquer un core dans le répertoire par défaut de cron, à savoir /etc/cron.d.

Ainsi, nous connaissons maintenant avec précision les conditions pour une exploitation réussie de cette faille :

  • l’appel système prctl avec l’option 2 pour créer un fichier core n’importe où ;
  • un mécanisme capable de traiter notre fichier, quel que soit son format : cron ou logrotate.

La conception de l’exploit suit ces 2 principes :

  • dans la mémoire de notre processus, on inscrit la chaˆıne qui sera traitée par cron ;

  • on change le répertoire de travail de notre processus pour aller dans /etc/cron.d ;

  • on fait planter notre propre processus, par exemple en lui envoyant un signal qui provoque un core (cf. kill(1)), comme SIGQUIT ;

  • on attend :

    cron then wakes up every minute, examining all stored crontabs, checking each command to see if it should be run in the current minute.

Plusieurs exploits sont publiquement disponibles et reposent tous sur ce mécanisme.

Derniers mots

Pour faire court, ajouter de nouvelles fonctionnalités, c’est sympa, mais encore faut-il être capable d’en mesurer toutes les conséquences. Comment ce bug est corrigé ? Simplement en retirant cette option du noyau. Entre temps, certains ont aussi jugé utile de rendre cron plus strict qu’il n’est par défaut, afin de le forcer à n’accepter que les fichiers ayant une syntaxe correcte.

Extrait du changelog sur une distribution Debian :

Handle errors when reading crontabs so that it stops reading entries in a crontab file after a syntax error. This prevents a rogue user from dumping binary files in /etc/cron.d/ or /var/spool/cron/crontabs/ and have them executed by cron (but binary files under /etc/cron.{daily,weekly,monthly} will still be processed as they are handled by run-parts. Thanks to Faidon Liambotis for the patch. (Closes: #378153)

Il faut bien comprendre que cela ne corrige en rien le problème puisqu’on l’a vu, il existe au moins un autre biais, par logrotate. Le seul correctif valable se situe au niveau du noyau avec le retrait de l’option.