本文涉及
技术原理都不是新
对研究人员没什么特别大
价值
不过对工程人员应急相应来说不失为
种新
思路方法
理解攻击向量
内核rookit通常以系统
为攻击目标
主要出于两个原因:
a.在内核态劫持系统
能以较小
代价控制整个系统
不必修太多东西;
b.应用层大多数
是
个或多个系统
区别形式
封装
更改系统
意味着其上层所有
都会被欺骗;
在kernel-2.4.27中大约有230多个系统
而kernel-2.6.9中大约有290多个系统
系统
个数取决于内核版本
完整
系统
列表可以在 /usr/
/asm/unistd.h头文件中获得
另外需要注意
是入侵者并不更改所有
系统
而只是替换其中
些比较有用
这些系统
如表
所示
他们可以被系统管理员及入侵检测系统(OS kernel级IDS)监视
可以用man命令得到每个系统
完整描述
call name Short description ID
---------------------------------------------------------------------------------------
sys_read used for reading from files 3
sys_write used for writing to files 4
sys_open used to create or open files 5
sys_getdents/sys_getdents64 used to list a content of directories(also /proc) 141/220
sys_call used for managing s 102
sys_query_module used for querying loaded modules 167
sys_uid/sys_getuid used for managing UIDs 23/24
sys_execve used for executing binary files 11
sys_chdir used to change the directory 12
sys_fork/sys_clone used to create a child process 2/120
sys_ioctl used to control devices 54
sys_kill used to send signal to processes 37
我们注意上表
系统
号
这些ID都是针对kernel-2.4.18-3
本文所有
例子都在Redhat7.3 kernel-2.4.18-3上通过测试
我们也可以在其他版本包括最新
2.6.x上用相似
步骤研究
区别的处可能在于2.6
些内部结构
比如系统
表
地址原来内含在系统
处理例程system_call中
现在改成在syscall_call
中
更改系统表
当前
系统
地址保存在系统
表中
位于操作系统为内核保留
内存空间(虚拟地址最高1GB)
系统
入口地址
存放顺序同/usr/
/asm/unistd.h中
排列顺序
按系统
号递增
在0x80软中断发生的前
对应
系统
号被压入eax寄存器
例如sys_write被
时
其对应
系统
ID:4会被压入eax
入侵者使用
第
种思路方法是:更改系统
表中
系统
地址
这样系统
发生时会跳转到攻击者自己编写
去执行
通过观察系统
表中
系统
入口地址
使用gdb我们可以比较容易检测到这种攻击行为
原始
系统
地址在内核编译阶段被指定
不会更改
通过比较原始
系统
地址和当前内核态中
系统
地址我们就可以发现系统
有没有被更改
原始
系统
地址在编译阶段被写入两个文件:
a.
.map该文件包含所有
符号地址
系统
也包含在内;
b.系统
化时首先被读入内存
内核映像文件vmlinux-2.4.x;
vmlinux-2.4.x文件通常以压缩
格式存放在/boot目录下
所以在比较的前必须解压这个文件
另
个问题是:我们
比较
前提是假设system.map及vmlinuz image都没有被入侵者更改
所以更安全
做法是在系统干净时已经创建这两个文件
可信任
拷贝
并创建文件
md5 hash
原文中也列举了
个内核模块[gcc -c scpr
.c -I/usr/src/`uname -r`/
/ ]使用该模块打印系统
地址
并自动写入syslog
这样可以进行实时
比较
在大多数被装载内核后门情况中
内核在系统
化的后才被更改
更改发生在加载了rootkit
module或者被植入直接读写/dev/kmem
on-the-fly kernel patch的后
而通常情况下rootkit并不更改vmlinuz和system.map 这两个文件
所以打印这两个文件中
符号地址就可以知道系统原始
系统
地址
系统当前运行中
系统
地址(可能被更改)可以同过/proc下
kcore文件得到
比较两者就知道结果
1.首先找出系统
表地址:
[root@rh8 boot]# cat .map-2.4.18-13 | grep sys_call_table c0302c30 D sys_call_table
2.使用nm命令可以打印出未被strip过
image文件中所有
符号地址:
[root@rh8 boot]# nm vmlinux-2.4.18-13 | grep sys_call_table
c0302c30 D sys_call_table
使用gdb可以打印出所有
系统
入口地址
这些对应
地址在内核源代码
entry.S文件中定义
例如:
entry 0 (0xc01261a0)是sys_ni_syscall系统
entry 1 (0xc011e1d0)是sys_exit系统
entry 2 (0xc01078a0)是sys_fork系统
#gdb /boot/vmlinux-2.4.*
(gdb) x/255 0xc0302c30
0xc0302c30 <sys_call_table>: 0xc01261a0 0xc011e1d0 0xc01078a0 0xc013fb70
0xc0302c40 <sys_call_table+16>: 0xc013fcb0 0xc013f0e0 0xc013f230 0xc011e5b0
0xc0302c50 <sys_call_table+32>: 0xc013f180 0xc014cb10 0xc014c670 0xc0107940
0xc0302c60 <sys_call_table+48>: 0xc013e620 0xc011f020 0xc014bcd0 0xc013e9a0
...
我们也可以通过系统
名打印出系统
地址:
(gdb) x/x sys_ni_syscall
0xc01261a0 <sys_ni_syscall>: 0xffffdab8
((gdb) x/x sys_fork
0xc01078a0 <sys_fork>: 0x8b10ec83
要打印出当前运行系统中
系统
地址我们必须给gdb加两个参数:
a.第
个参数是内核映像文件vmliux-2.4.x
b.第 2个参数是/proc/kcore 2进制文件
#gdb /boot/vmlinux-2.4.* /proc/kcore
(gdb) x/255x 0xc0302c30
0xc0302c30 <sys_call_table>: 0xc01261a0 0xc011e1d0 0xc01078a0 0xc88ab11a <<--
0xc0302c40 <sys_call_table+16>: 0xc013fcb0 0xc013f0e0 0xc013f230 0xc011e5b0
0xc0302c50 <sys_call_table+32>: 0xc013f180 0xc014cb10 0xc014c670 0xc0107940
0xc0302c60 <sys_call_table+48>: 0xc013e620 0xc011f020 0xc014bcd0 0xc013e9a0
...
我们注意到第
行最后
0xc88ab11a这个地址明显不正常
这是系统
号为3
系统
即sys_read (系统
从0开始)
我们说它不正常
显著标志是它
地址高于0xc8xxxxxx
Linux默认4GB线性地址
其中最高1GB0x00000000-0xffffffff为内核保留
当
个模块被插入内核时
vmalloc
为其分配
段地址空间
这个地址通常从0xc8800000开始...到这里已经很明显了吧?
系统劫持
劫持系统
和上
种思路方法区别的处在于:它并不直接修改系统
表中
入口地址
即指向每个系统
跳转指针
而是在想要hook
系统
的前加
段跳转代码
使执行流重定向到入侵者自己
内核态
这些被hook
系统
前部通常有call,jmp的类
汇编指令
要检测这种攻击
同样使用gdb加vmlinux-2.4.*及/proc/kcore两个参数
然后反汇编系统
:
#gdb /boot/vmlinux-2.4.* /proc/kcore
(gdb) disass sys_read
Dump of assembler code for function sys_read:
0xc013fb70 <sys_read>: mov $0xc88ab0a6,%ecx
0xc013fb73 <sys_read+3>: jmp *%ecx <<--
0xc013fb77 <sys_read+7>: mov %esi,0x1c(%esp,1)
0xc013fb7b <sys_read+11>: mov %edi,0x20(%esp,1)
0xc013fb7f <sys_read+15>: mov $0xfffffff7,%edi
...
我们注意"mov $0xc88ab0a6,%ecx -- jmp *%ecx"这两条指令
他跳转到了其他
地方去执行了
然后再来看
下被hook的前
系统
指令:
#gdb /boot/vmlinx-2.4.*
(gdb) disass sys_read
Dump of assembler code for function sys_read:
0xc013fb70 <sys_read>: sub $0x28,%esp
0xc013fb73 <sys_read+3>: mov 0x2c(%esp,1),%eax
0xc013fb77 <sys_read+7>: mov %esi,0x1c(%esp,1)
0xc013fb7b <sys_read+11>: mov %edi,0x20(%esp,1)
0xc013fb7f <sys_read+15>: mov $0xfffffff7,%edi
...
看到了吧
不
样
更改系统处理例程
入侵者可能修改
些重要
内核
比如系统
处理例程system_call
顾名思义
这个
对用户请求
系统
作出响应
在系统
表中寻找对应
入口地址
然后跳转到那里执行
这个
中保存了系统
表
地址
攻击者能做什么呢?另辟
块内存空间
在那里攻击者伪造自己
系统
表
然后修改system_call
中
系统
表地址指向那里就可以了
通过反汇编system_call
可以找出系统
表
地址:
(gdb) disass system_call
Dump of assembler code for function system_call:
0xc01090dc <system_call>: push %eax
0xc01090dd <system_call+1>: cld
0xc01090de <system_call+2>: push %es
0xc01090df <system_call+3>: push %ds
0xc01090e0 <system_call+4>: push %eax
0xc01090e1 <system_call+5>: push %ebp
0xc01090e2 <system_call+6>: push %edi
0xc01090e3 <system_call+7>: push %esi
0xc01090e4 <system_call+8>: push %edx
0xc01090e5 <system_call+9>: push %ecx
0xc01090e6 <system_call+10>: push %ebx
0xc01090e7 <system_call+11>: mov $0x18,%edx
0xc01090ec <system_call+16>: mov %edx,%ds
0xc01090ee <system_call+18>: mov %edx,%es
0xc01090f0 <system_call+20>: mov $0xffffe000,%ebx
0xc01090f5 <system_call+25>: and %esp,%ebx
0xc01090f7 <system_call+27>: testb $0x2,0x18(%ebx)
0xc01090fb <system_call+31>: jne 0xc010915c <tracesys>
0xc01090fd <system_call+33>: cmp $0x100,%eax
0xc0109102 <system_call+38>: jae 0xc0109189 <badsys>
0xc0109108 <system_call+44>: call *0xc0302c30 (,%eax,4) <<--系统表地址
0xc010910f <system_call+51>: mov %eax,0x18(%esp,1)
0xc0109113 <system_call+55>: nop
End of assembler dump.
注意:上面
输出中显示
是
个正常
系统
表地址
实用工具
种思路方法是使用基于主机
入侵检测系统HIDS实时监控重要
内核结构
比如使用Samhain工具
可以监视系统
表、IDT等
在“Host Integrity Monitoring: Best Practices for Deployment”
文中有相关描述
译者注
本文提及
思路方法在kstat2.4版中都有代码
实现
可以参阅kstat/2.4/src/syscall.c
使用gdb是
种手工检测思路方法
它能解决
问题是检测系统是否被更改
至于如何找出内核rootkit还需要
些工具
比如madsys在phrack60上
module_hunter.c
有2.4和2.6
版本
grip2、coolq对其做了
些修改
并且该代码不断完善中
延伸阅读
最新评论