linux不同版本间驱动加载方法.doc_第1页
linux不同版本间驱动加载方法.doc_第2页
linux不同版本间驱动加载方法.doc_第3页
linux不同版本间驱动加载方法.doc_第4页
linux不同版本间驱动加载方法.doc_第5页
已阅读5页,还剩28页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

WebZine Introduction - by root Flashsky访谈 - by flashsky Struts2框架安全缺陷 - by kxlzx 重谈IP欺骗技术 - by papaya Fuzz客户端存储对象,寻找client ddos - by woyigui 应用软件缺陷利用的一点心得(Webkit篇) - by wushi Bypassing Linux kernel module version check - by wzt ACS - Active Content Signatures - by Eduardo Vela Nava 卡巴虚拟机启发式查毒的绕过方法 - by dangdang Bypassing Linux kernel module version checkBy wzt1、 为什么要突破模块验证2、 内核是怎么实现的3、 怎样去突破4、 总结5、 参考6、 附录1、 为什么要突破模块验证 Linux内核版本很多,升级很快,2个小内核版本中内核函数的定义可能都不一样,为了确保不一致的驱动程序导致kernel oops, 开发者加入了模块验证机制。它在加载内核模块的时候对模块进行校验, 如果模块与主机的一些环境不一致,就会加载不成功。 看下面一个例子,它简单的输出当期系统中的模块列表:#include #include #include #include #include #include MODULE_LICENSE(GPL);MODULE_AUTHOR(wzt);struct module *m = &_this_module;int print_module_test(void) struct module *mod; list_for_each_entry(mod, &m-list, list) printk(%sn, mod-name); return NULL;static int list_print_init(void) printk(load list_print module.n); print_module_test(); return 0;static void list_print_exit(void) printk(unload list_print module.n);module_init(list_print_init);module_exit(list_print_exit);我们在centos5.3环境中编译一下:rootlocalhost list# uname -aLinux localhost.localdomain 2.6.18-128.el5 #1 SMP Wed Jan 21 10:44:23 EST 2009 i686 i686 i386 GNU/Linux然后拷贝到另一台主机centos5.1xen上:rootlocalhost # uname -aLinux localhost.localdomain 2.6.18-53.el5xen #1 SMP Mon Nov 12 03:26:12 EST 2007 i686 i686 i386 GNU/Linux用insmod加载:rootlocalhost # insmod list.koinsmod: error inserting list.ko: -1 Invalid module format报错了,在看下dmesg的信息:rootlocalhost # dmesg|tail -n 1list: disagrees about version of symbol struct_module先不管这是什么, 总之我们的模块在另一台2.6.18的主机中加载失败。 通常的做法是要在主机中对源代码进行编译,然后才能加载成功, 但是如果主机中缺少内核编译环境的话, 我们的rootkit就不能编译, 也不能安装在主机之中,这是多么尴尬的事情:)。 没错, 这就是linux kernel开发的特点, 你别指望像windows驱动一样,编译一个驱动, 然后可以满世界去装_. 一些rootkit开发者抛弃了lkm类型rk的开发, 转而去打kmem, mem的注意,像sk,moodnt这样的rk大家都喜欢, 可以在用户层下动态patch内核,不需要编译环境, wget下来,install即可。 但是它也有很多缺点,比如很不稳定,而且在2.6.x后内核已经取消了kmem这个设备, mem文件也做了映射和读写的限制。 rk开发者没法继续sk的神话了。反过来, 如果我们的lkm后门不需要编译环境,也可以达到直接insmod的目的,这是件多么美好的事情,而且lkm后门更加稳定,还不用像sk在内核中添加了很多自己的数据结构。2、内核是怎么实现的 我们去看看内核在加载模块的时候都干了什么, 或许我们可以发现点bug, 然后做点手脚,欺骗过去:) grep下dmesg里的关键字, 看看它在哪个文件中:rootlocalhost linux-2.6.18# grep -r -i disagrees about kernel/kernel/module.c: printk(%s: disagrees about version of symbol %sn,2.6.18/kernel/module.c:insmod调用了sys_init_module这个系统调用, 然后进入load_module这个主函数,它解析elf格式的ko文件,然后加载到内核中:/* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */static struct module *load_module(void _user *umod, unsigned long len, const char _user *uargs). if (!check_modstruct_version(sechdrs, versindex, mod) err = -ENOEXEC; goto free_hdr; modmagic = get_modinfo(sechdrs, infoindex, vermagic); /* This is allowed: modprobe -force will invalidate it. */ if (!modmagic) add_taint(TAINT_FORCED_MODULE); printk(KERN_WARNING %s: no version magic, tainting kernel.n, mod-name); else if (!same_magic(modmagic, vermagic) printk(KERN_ERR %s: version magic %s should be %sn, mod-name, modmagic, vermagic); err = -ENOEXEC; goto free_hdr; .check_modstruct_version就是用来计算模块符号的一些crc值,不相同就会出现我们在dmesg里看到的“disagrees about version of symbol”信息。 get_modinfo取得了内核本身的vermagic值,然后用same_magic函数和内核的vermagic去比较,不同也会使内核加载失败。 所以在这里,我们看到内核对模块验证的时候采用了2层验证的方法:模块crc值和vermagic检查。继续跟踪check_modstruct_version, 现在的内核默认的都开启了CONFIG_MODVERSIONS, 如果没有指定这个选项,函数为空,我们的目的是要在As, Centos下安装模块,redhat不是吃干饭的, 当然开了MODVERSIONS选项。static inline int check_modstruct_version(Elf_Shdr *sechdrs, unsigned int versindex, struct module *mod) const unsigned long *crc; struct module *owner; if (!_find_symbol(struct_module, &owner, &crc, 1) BUG(); return check_version(sechdrs, versindex, struct_module, mod, crc);_find_symbol找到了struct_module这个符号的crc值,然后调用check_version去校验:static int check_version(Elf_Shdr *sechdrs, unsigned int versindex, const char *symname, struct module *mod, const unsigned long *crc) unsigned int i, num_versions; struct modversion_info *versions; /* Exporting module didnt supply crcs? OK, were already tainted. */ if (!crc) return 1; versions = (void *) sechdrsversindex.sh_addr; num_versions = sechdrsversindex.sh_size / sizeof(struct modversion_info); for (i = 0; i name, symname); DEBUGP(Found checksum %lX vs module %lXn, *crc, versionsi.crc); return 0; /* Not in modules version table. OK, but that taints the kernel. */ if (!(tainted & TAINT_FORCED_MODULE) printk(%s: no version for %s found: kernel tainted.n, mod-name, symname); add_taint(TAINT_FORCED_MODULE); return 1;它搜寻elf的versions小节, 循环遍历数组中的每个符号表,找到struct_module这个符号,然后去比较crc的值。现在有个疑问, versions小节是怎么链接到模块的elf文件中去的呢? 在看下编译后的生成文件, 有一个list.mod.crootlocalhost list# cat list.mod.c#include #include #include MODULE_INFO(vermagic, VERMAGIC_STRING);struct module _this_module_attribute_(section(.gnu.linkonce.this_module) = .name = KBUILD_MODNAME, .init = init_module,#ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module,#endif;static const struct modversion_info _versions_attribute_used_attribute_(section(_versions) = 0x89e24b9c, struct_module , 0x1b7d4074, printk ,;static const char _module_depends_attribute_used_attribute_(section(.modinfo) =depends=;MODULE_INFO(srcversion, 26DB52D8A56205333D414B9);这个文件是模块在编译的时候,调用了linux-2.6.18/scripts/modpost这个文件生成的。里面增加了2个小节.gnu.linkonce.this_module和_versions。 _versions小节的内容就是一些字符串和值组成的数组,check_version就是解析这个小节去做验证。 这里还有一个MODULE_INFO宏用来生成模块的magic字符串,这个在以后的vermagic中要做验证。先看下vermagic的格式:rootlocalhost list# modinfo list.kofilename: list.koauthor: wztlicense: GPLsrcversion: 26DB52D8A56205333D414B9depends: vermagic: 2.6.18-128.el5 SMP mod_unload 686 REGPARM 4KSTACKS gcc-4.1这里可以看到vermagic跟内核版本,smp,gcc版本,内核堆栈大小都有关。/* First part is kernel version, which we ignore. */static inline int same_magic(const char *amagic, const char *bmagic) amagic += strcspn(amagic, ); bmagic += strcspn(bmagic, ); return strcmp(amagic, bmagic) = 0;same_magic忽略了对内核版本的判断, 直接比较后面的值。3、怎样去突破 知道了内核是怎么实现的了, 下面开始想办法绕过这些验证:) 3.1 怎么突破crc验证: 在仔细看下代码: for (i = 0; i name, symname); DEBUGP(Found checksum %lX vs module %lXn, *crc, versionsi.crc); return 0; /* Not in modules version table. OK, but that taints the kernel. */ if (!(tainted & TAINT_FORCED_MODULE) printk(%s: no version for %s found: kernel tainted.n, mod-name, symname); add_taint(TAINT_FORCED_MODULE); return 1;check_version在循环中只是在寻找struct_module符号, 如果没找到呢? 它会直接返回1! 没错, 这是一个逻辑bug,在正常情况下,module必会有一个struct_module的符号, 这是modpost生成的。如果我们修改elf文件,把struct_module这个符号改名,岂不是就可以绕过crc验证了吗? 先做个实验看下:.mod.c是由modpost这个工具生成的, 它在linux-2.6.18/scripts/Makefile.modpost文件中被调用, 去看下:PHONY += _modpost_modpost: $(wildcard vmlinux) $(modules:.ko=.o) FORCE $(call cmd,modpost)我们用一个很土的方法, 就是在编译模块的时候,modpost生成.mod.c文件后, 暂停下编译,sleep 30秒吧,我们用这个时间去改写下.mod.c, 把struct_module换个名字。PHONY += _modpost_modpost: $(wildcard vmlinux) $(modules:.ko=.o) FORCE $(call cmd,modpost) sleep 30随便将struct_module改个名:rootlocalhost list# cat list.mod.c#include #include #include MODULE_INFO(vermagic, VERMAGIC_STRING);struct module _this_module_attribute_(section(.gnu.linkonce.this_module) = .name = KBUILD_MODNAME, .init = init_module,#ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module,#endif;static const struct modversion_info _versions_attribute_used_attribute_(section(_versions) = 0x89e24b9c, stauct_module , 0x1b7d4074, printk ,;static const char _module_depends_attribute_used_attribute_(section(.modinfo) =depends=;MODULE_INFO(srcversion, 26DB52D8A56205333D414B9);我们是在centos5.3下编译的, 然后拷贝到centos5.1下, 在执行下insmod看下:rootlocalhost # insmod list.korootlocalhost # dmesg|tailata_piixlibatasd_modscsi_modext3jbdehci_hcdohci_hcduhci_hcd成功了! 这跟我们预期的一样, 我们用这个逻辑bug绕过了模块的crc验证! 这个bug直到2.6.31版本中才得到修正。 我们可以用这种方法在redhat主机中任意安装模块了。 那么怎样绕过在2.6.31以后的内核呢?看下它是怎么修补的:for (i = 0; i name, symname);return 0;bad_version:printk(%s: disagrees about version of symbol %sn, mod-name, symname); return 0;如果没找到struct_module也会返回0, 这样我们就必须将struct_module的值改为正确后, 才能继续安装。如何找到模块符号的crc值呢? 我们可以去找目标主机中那些已被系统加载的模块的crc值,如ext3文件系统的模块, 自己写个程序去解析elf文件, 就可以得到某些符号的crc值了。 还有没有更简单的方法呢?去/boot目录下看看,symvers-2.6.18-128.el5.gz貌似和crc有关,gunzip解压后看看:rootlocalhost boot# grep struct_module symvers-2.6.18-128.el5 0x89e24b9c struct_module vmlinux EXPORT_SYMBOL原来内核中所有符号的crc值都保存在这个文件中。如何改写struct_module的值呢,可以用上面那个土方法,或者自己写程序去解析elf文件, 然后改写其值。本文最后附上一个小程序用来修改elf的符号和crc值。3.2 如何突破vermagic验证: 如果我们用list.mod.c中的做法, 用MODULE_INFO宏来生成一个与目标主机相同的vermagic呢? 答案是否定的,gcc链接的时候会把modinfo小节链接在最后,加载模块的时候还是会读取第一个.modinfo小节。我们可以用上面那种很土的方法, 先用modinfo命令得到目标主机中某个模块的信息:rootlocalhost list# modinfo /lib/modules/2.6.18-128.el5/kernel/fs/ext3/ext3.kofilename: /lib/modules/2.6.18-128.el5/kernel/fs/ext3/ext3.kolicense: GPLdescription: Second Extended Filesystem with journaling extensionsauthor: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Tso and otherssrcversion: B048AC103E5034604A721C5depends: jbdvermagic: 2.6.18-128.el5 SMP mod_unload 686 REGPARM 4KSTACKS gcc-4.1module_sig: 883f3504977495e4f3f897cd3dced211288209f551cc1da557f96ea18d9a4efd6cfb0fc2612e009c8845fd776c825d586f492ceab19e17b2319da8f然后在用那个很土的方面, 将.mod.c中vermagic值进行修改。还有一种直接修改elf文件的方法,附录在本文后面。4、总结 前面有一点没有提到, 就是某些内核版本的相同接口的函数代码可能已经变化, 这样在使用这项技术的时候, 最好在同一个大内核版本使用。你也可能感觉要想跨平台安装模块有些麻烦, 这里还有2个方法, 作为一个专业 搞渗透的人来说,他会自己在本地装很多发行版本的linux,特别是root掉一台主机后,会在本地装一个一模一样 的发行版本,smp、kernel stack size、gcc version都一样。在本地机器装上开发环境,这样编译出来的模块 也是可以直接装到目标主机上的,但这很麻烦,因为linux有太多的发行版本了:), 另一个方法就是自己 装一个linux,编译下内核,然后将build后的开发包集成到自己的后门里, 压缩后大概几m。 然后传到主机 去解压,编译。庆幸的是,现在大多数主机中都有内核开发环境, 直接去主机编译就ok了。5、参考 【1】 linux kernel source code 【2】 module injection in 2.6 kernel - Coolq /index.php?act=magazine&do=view&mid=25336、附录/* * Linux kernel module fucker * * by wzt * */#include #include #include #include #include #include #include #include #define MODULE_NAME_LEN (64 - sizeof(unsigned long)struct modversion_info unsigned long crc; char nameMODULE_NAME_LEN;Elf32_Ehdr *ehdr = NULL;Elf32_Phdr *phdr = NULL;Elf32_Shdr *shdr = NULL;Elf32_Shdr *shstrtab = NULL;Elf32_Sym *dynsym_ptr = NULL;Elf32_Sym *symtab_ptr = NULL;Elf32_Sym *dynstr_ptr = NULL;char *Real_strtab = NULL;char *dynstr = NULL;char *strtab_ptr = NULL;char dynstr_buffer2048; char strtab_buffer4096; char *real_strtab = NULL;unsigned int shstrtab_off, shstrtab_len, shstrtab_num;unsigned int strtab_off, strtab_size;int elf_fd;struct stat f_stat; void usage(char *pro) fprintf(stderr, usage: %s nn, pro); fprintf(stderr, -w -vtCheck vermgaic in module.n); fprintf(stderr, -w -ctCheck crc value in module.n); fprintf(stderr, -s -v tSet vermagic in module.n); fprintf(stderr, -s -ctSet crc value in module.n); exit(0);int init_load_elf(char *elf_file) char buff1024; elf_fd = open(elf_file, O_RDWR); if (elf_fd = -1) perror(open); return 0; fprintf(stderr, + Open %s ok.n, elf_file); if (fstat(elf_fd, &f_stat) = -1) perror(fstat); return 0; ehdr = (Elf32_Ehdr *)mmap(NULL, f_stat.st_size, PROT_WRITE|PROT_READ, MAP_SHARED, elf_fd, 0); if(ehdr = MAP_FAILED) perror(mmap); return 0; phdr = (Elf32_Phdr *)(unsigned long)ehdr + ehdr-e_phoff); shdr = (Elf32_Shdr *)(unsigned long)ehdr + ehdr-e_shoff); shstrtab = &shdrehdr-e_shstrndx; shstrtab_off = (unsigned int)shstrtab-sh_offset; shstrtab_len = shstrtab-sh_size; real_strtab = (char *)( (unsigned long)ehdr + shstrtab_off ); printf(+ .Shstrtab Size :0x%x,%dn, shstrtab-sh_size, shstrtab-sh_name); printf(+ .Shstrtab Off: 0x%xn, shstrtab_off); return 1;int display_module_crc_info(void) struct modversion_info *versions; char *buff = NULL; unsigned int version_off, version_size, num_versions; int i, j; buff = (char *)malloc(shstrtab_len + 2); if (!buff) fprintf(stderr, - Malloc failed.n); return 0; memcpy(buff, real_strtab, shstrtab_len + 1); for (i = 0 ; i e_shnum ; i+) if (!strcmp(buff + shdri.sh_name,_versions) printf(+ found section %s.n, buff + shdri.sh_name); version_off = (unsigned int)shdri.sh_offset; version_size = (unsigned int)shdri.sh_size; printf(+ version off: 0x%xn, version_off); printf(+ version size: 0x%xn, version_size); break; printf(+ %x,%xn, (unsigned long)ehdr + version_off, shdri.sh_addr); versions = (void *)(unsigned long)ehdr + version_off); num_versions = version_size / sizeof(struct modversion_info); printf(+ num_versions: %dn, num_versions); for (j = 0; j num_versions; j+) printf(+ %s:0x%08x.n, , versionsj.crc); free(buff); return 1;int set_module_crc_info(void) struct modversion_info *versions; char *buff = NULL; unsigned int version_off, version_size, num_versions; int i, j; buff = (char *)malloc(shstrtab_len + 2); if (!buff) fprintf(stderr, - Malloc failed.n); return 0; memcpy(buff, real_strtab, shstrtab_len + 1); for (i = 0 ; i e_shnum ; i+) if (!strcmp(buff + shdri.sh_name,_versions) printf(+ found section %s.n, buff + shdri.sh_name); version_off = (unsigned int)shdri.sh_offset; version_size = (unsigned int)shdri.sh_size; printf(+ version off: 0x%xn, version_off); printf(+ version size: 0x%xn, version_size); brea

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论