ChinaUnix.net 首页 | 博客 | Linux | 论坛 | 人才 | 培训 | 知识库 | 资料 | 读书 | 手册 | 精华 | 下载 | 沙龙 | 搜索
Linux首页 | Linux论坛 | 论坛精华 | 开源新闻 | 技术文章 | 专题专栏 | 新手指南 | 迁移方案 | 产品方案 | 开源项目 | 开源图书 | 软件下载 | 人才招聘 | Linux博客
  搜索

  产品与方案
·中科红旗全面打造现代化邮政体系
·红旗助力“网上审批服务” 推动电子政务
·红旗正版化开创呼和浩特网吧建设新起点
·红旗Linux助信息产业部邮件服务器“快跑”
·中标普华Linux 为电子政务信息化保驾护航
·中标普华Linux助力基金产业
·中标普华Office率先支持UOF标准
·中标普华邮件系统助力西藏政府信息化建设
·红旗Linux助力国库集中支付系统改革
·红旗助中信卫星 掀起GIS通信应用风暴
·红旗软件助力烟草总局 全面建设“数字烟草”
·红旗助力“信访阳光工程”打造畅通信访渠道
·红帽联合FIS发布下一代实时核心银行平台
·红旗助力金盾 打造全无忧出入境信息系统
·红旗Linux全力打造中国邮政总局名址信息库
·爱尔兰证交所从Unix迁移到红帽企业Linux
·一流的意大利银行选择使用红帽企业Linux
·PLUS Finanzservice选择使用红帽企业Linux
·红帽助力TransACT Communications 公司
·法国零售业巨头Lapeyre采用Redhat Linux
·旅游预订网站选择使用红帽企业Linux
·马哈拉施特拉邦政府的红帽解决之道
·美国联邦政府案例
·红帽为慕尼黑展览会提供现代化集群系统
·Yuba郡用开源软件和红帽产品提高了效率
·红帽企业Linux助印度理工建立高性能计算中心
·采用红帽Linux 将系统维护时间缩短了65%
·从UNIX迁移到Linux使Peñoles公司获益非浅
·Hikal公司用红帽企业Linux开展任务关键的ERP项目
·KDE3.5.4新版本发布
·芝加哥商业交易所从Unix向Linux迁移
·南方基金管理有限公司成功案例 Red Hat Linux
·广东北电通讯设备有限公司成功案例
·挪威国家石油公司从UNIX迁移到红帽Linux,成本减半
·中央电视台CCTV动画部案例 Red Hat Linux

  图书

鸟哥的Linux私房菜基础学..


Linux程序设计.第3版


Linux设备驱动开发详解


  下载
·Endian Firewall
·linux kernel(Linux 内核)
·CentOS
·Fedora Core 6
·Scientific Linux
·Slackware 11.0
·Gentoo Linux
·ubuntu-6.10-i386服务器版本
·ubuntu-6.10-amd64服务器版
·ubuntu-6.10-i386桌面版
·ubuntu-6.10-amd64桌面版
·Engarde Linux
您的位置: Linux时代 > 技术文档 > 系统安全 >

揭示恶意的Linux内核模块是如何工作

日期:2007-03-08 作者:Linux时代 来自:linux.chinaunix.net


如果在入侵事件调查中,传统的工具完全失效了,你该怎么办?当我在对付入侵者已经加载的内核模块时,就陷入了这种困境。由于从用户空间升级到了内核空间,LKM方式的入侵改变了以往使用的入侵响应的技术。一旦内核空间遭破坏,影响将覆盖到整个用户空间,这样入侵者无须改动系统程序就能控制他们的行为。而用户即使将可信的工具包上传到被入侵的主机,这些工具也不再可信。下面我将揭示恶意的内核模块如何工作,并且给出一些我开发的对付此类入侵的工具。

LKM概述

LKM的存在对系统管理员是个福音,对入侵检测却是个噩梦。LKM最初被设计用来无须重新启动而改变运行中的内核,从而提供一些动态功能。动态内核提供了对诸如新文件系统类型和网卡等设备的额外支持。此外,由于内核模块能够访问内核的所有调用和存储区,它能不受控制地改动整个操作系统的各个部位,因而所有调用和内存常驻的结构都有被恶意内核模块修改的危险。

LKM的一个臭名昭著的例子是knark。一旦knark编译并加载到入侵主机,将改变系统调用表从而改变操作系统的行为。系统调用表常驻在内核空间,基本上是提供给用户级别程序访问操作系统的入口。大多数Unix系统在手册的第二部分给出syscalls的正式定义。一旦内核作为用户空间运行,OS将把命令行上运行的所有命令和调用映像到系统调用表中。因此当knark改变系统调用表时也就改变了用户命令的执行。knark改动了以下的重要系统调用。

* getdents - 获得目标路径的目录项内容(即文件和子目录)。通过修改这个调用,knark实现对用户程序隐藏文件和目录。

* kill - 向进程发送信号,通常是杀掉进程。修改过的调用将使用无用的信号31,触发设置进程为"hidden"状态。当进程在hidden状态时,它在/proc中的纪录被删除,从而实现了对ps命令隐身。信号32被用来解除隐藏状态。

* read - 读取目标文件的内容。knark通过修改此调用实现对netstat隐藏入侵者的连接。

* ioctl - 改变文件和设备的状态。通过修改此调用,knark能够隐藏网卡的混杂位,同时在调用中插入了隐藏文件的函数。

* fork - 派生新进程。knark修改用来隐藏一个隐藏的父进程所派生的所有子进程。

* execve - 执行一个程序。每次用户在命令行下输入命令时调用。一旦此调用被劫持,内核模块可以控制命令的选择和运行。knark使入侵者可以把一个程序指向另一个,如同符号连接一样,而不留下罪证。knark控制了execve后,任何你希望执行的程序都有可能是入侵者的替代品。

* settimeofday - 设置系统时间。knark用来监控预定的时间。当这些预定时间之一被送给此系统调用时,knark可以触发某些管理任务或者立即赋予当前用户root的用户和组id。这样就无需更改到suid的shell而直接获得root权限。

由于系统调用被更改,那些管理工具的功能也被更改了。netstat将永远不报告网卡的混杂模式,来自特定地点的连接也被隐藏。ps和top命令不会报告隐藏的进程,因为/proc中没有信息。ls将跳过隐藏的文件和目录。所有这些,都是因为此类工具依靠操作系统提供信息,而入侵者在控制了操作系统后就能够向来自用户空间的请求反馈虚假情报,并且无需改动netstat,ps,top和 ls程序的二进制文件。因此,tripwire一类的文件系统校验工具对这类工具将失效,也无法防备knark的执行重定向功能。如果入侵者将 hackme连接到cat上,每次cat被调用,实际上是hackme在执行。这样,cat仍然保留在系统上,md5校验码也没有改变,但执行的功能却改变了。

更糟糕的是,将一套新的工具上传到被knark入侵的主机也无济于事。即使是可信的工具一样要使用系统调用,于是他们也变得不再可信。目前还无法绕过入侵者在内核级别的陷阱,除非我们也进入内核空间。基于此,我开发了检测系统是否安装了恶意LKM的工具。

之前有一点我们没有提及,lsmod会报告装载了knark.o模块。不幸的是,入侵者能轻易的将此信息抹去。knark同时还包括了另一个LKM叫做modhide,能够隐藏自身以及上一个模块。一旦模块隐藏,如果不重启动机器就无法卸载,而且没有简单的方法检测到模块的加载,所有的相关信息都不见了。正如之前介绍的,knark的所有功能令其成为终极秘密武器。

预防方法

阻止LKM破坏显然是最佳解决方案。我们有几种方法能够提前预防LKM。可以通过保护系统调用表来预防大部分的恶毒LKM。我们可以构造一个简单的LKM,定时的或者在其他模块加载时监控系统调用表。如果它发现系统调用表改变了,可以通知系统管理员甚至将调用表修改回原来的值。下面的例子能很好的工作在Linux 2.2和2.4上。如果你的机器有超过一个处理器,可以用如下命令编译:gcc -D __SMP__ -c syscall_sentry.c。如果是单处理器,去掉-D __SMP__就行了。编译成功后,用insmod加载。具体参看下面的例子。

  
  /* 
  * This LKM is designed to be a tripwire for the sys_call_table. 
  */ 
  #define MODULE_NAME "syscall_sentry" 
  /* This definition is the time between periodic checks. */ 
  #define TIMEOUT_SECS 10 
  #define MODULE 
  #define __KERNEL__ 
  #include<linux/module.h> 
  #include<linux/config.h> 
  #include<linux/version.h> 
  #include<linux/kernel.h> 
  #include<linux/sys.h> 
  #include<linux/param.h> 
  #include<linux/sched.h> 
  #include<linux/timer.h> 
  #include<sys/syscall.h> 
  /* This function is a simple string comparison function */ 
  static int mystrcmp( const char *str1, const char *str2) 
  { 
  while(*str1 && *str2) 
  if (*(str1++) != *(str2++)) 
  return -1; 
  return 0; 
  } 
  /* This function builds a timer struct for versions of Linux 
  * less than Linux 2.4. It is used to set a timer 
  */ 
  #if linux_VERSION_CODE < KERNEL_VERSION(2,4,0) 
  /* Initializes a timer */ 
  void init_timer(struct timer_list * timer) 
  { 
  timer->next = NULL; 
  timer->prev = NULL; 
  } 
  #endif 
  /* This is our timer */ 
  static struct timer_list syscall_timer; 
  /* This is the system’s syscall table */ 
  extern void *sys_call_table[]; 
  /* This is the saved, valid syscall table */ 
  static void *orig_sys_call_table[ NR_syscalls ]; 
  /* This function is needed to protect yourself */ 
  static unsigned long (*orig_init_module) (const char *, struct module*); 
  /* This function checks the syscalls for changes 
  * and changes them back to the original if it has 
  * been changed. 
  */ 
  static int check_syscalls( void ) 
  { 
  int i; 
  /* Add a new timer for our next check */ 
  del_timer( &syscall_timer ); 
  init_timer( &syscall_timer ); 
  syscall_timer.function = (void *)check_syscalls; 
  syscall_timer.expires = jiffies + TIMEOUT_SECS * HZ; 
  add_timer( &syscall_timer ); 
  for ( i = 0; i < NR_syscalls - 1; i++ ) 
  { 
  if (orig_sys_call_table[i] != sys_call_table[i]) 
  { 
  printk(KERN_INFO " SysCallSentry - sys_call_table has been 
  modified in entry %d! ", i); 
  sys_call_table[i] = orig_sys_call_table[i]; 
  } 
  } 
  return 1; 
  } 
  /* Check sys_call_table anytime a new module is loaded. */ 
  static int long sys_init_module_wrapper( const char *name, struct 
  module *mod ) 
  { 
  int i; 
  int res = (*orig_init_module)(name,mod); 
  for ( i = 0; i < NR_syscalls - 1; i++ ) 
  { 
  if (orig_sys_call_table[i] != sys_call_table[i]) 
  { 
  printk( KERN_INFO " SysCallSentry - sys_call_table has been 
  modified in entry %d! ", i); 
  sys_call_table[i] = orig_sys_call_table[i]; 
  } 
  } 
  return res; 
  } 
  /* Module Init Code */ 
  static int init_module (void) 
  { 
  int i; 
  printk(KERN_INFO " SysCallSentry Inserted "); 
  /* Initiate the periodic timer */ 
  init_timer( &syscall_timer ); 
  /* Save the old values of the sys_call_table */ 
  orig_init_module = sys_call_table[SYS_init_module]; 
  /* Wrap the init_module syscall. This will check to see 
  * if any calls have been altered when a new module loads. 
  */ 
  sys_call_table[SYS_init_module] = sys_init_module_wrapper; 
  for ( i=0; i < NR_syscalls - 1; i++ ) 
  { 
  orig_sys_call_table[i] = sys_call_table[i]; 
  } 
  /* Start our first check */ 
  check_syscalls(); 
  return(0); 
  } 
  /* Module Cleanup Code */ 
  static void cleanup_module (void) 
  { 
  /* Return system status to the original */ 
  sys_call_table[SYS_init_module] = orig_init_mo

本文被浏览



 相关新闻

Mozilla 曝9缺陷 黑客运行恶意代码窃取资料2005-04-21 10:28:53
PHP惊现安全漏洞 易执行恶意代码和DoS攻击2005-04-21 10:25:11
LVM 五分钟教程2006-11-03 10:57:32
lvm的介绍和常用功能流程实验2006-10-16 14:12:04
使用kgdb调试linux内核及内核模块2006-08-03 10:26:37
linux内核模块和驱动程序的编写2001-08-30 15:00:00


 相关评论
关于我们 | 联系方式 | 广告合作 | 诚聘英才 | 网站地图 | 免费注册

Copyright © 2001-2006 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们

京ICP证041476号