设为首页 友情链接
在线留言 发表文章
加入收藏 广告联系

刺猬首页

| 专案技术 | 网络技术 | 图形图象 | 网络编程 | 网页设计 | 操作系统 | 服务器 | 技术白皮书 | 在线实验室 | 刺猬论坛 |
小说专版  | 数据库 | 设计赏析 | 存储频道 | 网络安全 | 私服架设 |  Solaris | 网站评估 | PC维护技巧 | 下载中心 | 博 客 |
专   题: | Linux | java | cisco | 防病毒 | 刀片 | SOA | iscsi | ASP.NET | SQL | Oracle |
您现在的位置: IT公社 IT community >> Linux专题 >> 内核研究 >> 教程正文 用户登录 新用户注册
专 题 栏 目
最 新 热 门
最 新 推 荐
相 关 文 章
通过振动向Linux ThinkP…
精华推荐:让Linux“开口…
甲骨文新推Oracle Linux…
Linux下高可用/可伸缩Se…
Linux下用明智的账户管理…
Linux中虚拟化方法、技术…
Linux中虚拟化方法、技术…
精华推荐:Linux的向“心…
NEC差异化战略推高端Lin…
让Linux更安全——安全含…
  Linux可加载内核模块:入侵响应分析         
Linux可加载内核模块:入侵响应分析
 

如果在入侵事件调查中,传统的工具完全失效了,你该怎么办?当我在对付入侵者已经加载的内核模块时,就陷入了这种困境。由于从用户空间升级到了内核空间,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
Linux联盟收集整理

频道声明:本频道的文章除部分特别声明禁止转载的专稿外,可以自由转载.但请务必注明出出处和原始作者 文章版权归本频道与文章作者所有.对于被频道转载文章的个人和网站,我们表示深深的谢意。

原始作者:佚名 录入时间:2007-2-4 20:27:19
信息来源:不详 投稿信箱:itqoo@126.com
教程录入:itqoo    责任编辑:itqoo 
  • 上一个教程:

  • 下一个教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    - 关于我们 - 合作伙伴 - 友情链接 - 广告刊登 - 投稿热线 - 在线留言版权声明联系方式 -
    IT公社版权所有 粤ICP备05127012号
    Copyrigh@2005-2006 itqoo.com.Inc All Rights Reserved  推荐分辨率 1024*768
    联系站长:E-Mail:itqoo@126.com     MSN:urchincc@hotmail.com    QQ:点击这里给我发消息
    特别感谢:亿太网络提供空间支持