联机的Linux的系统分析(第二部分)(第一版)
======================================================================
----原著:Mariusz Burdach
翻译:西安邮电学院电科0101 徐兆元 02985384887
译文版权: 徐兆元 西安邮电学院156# [email protected]/[email protected]
2004.8.15
转载请通知
======================================================================
1.第二部分简介
在本文的第一部分中,我们讨论了一些在分析非安全系统时候的准备工作和必须的步骤.现在我们继续来分析系统以找出系统是恶意代码的所在,然后讨论从被传送到远程主机上的数据中寻找的方法.
纪录:
一些读者在阅读完本文的第一部分后指出:在从非安全系统是传送任何数据之前,受托的shell应该被运行.我想这是一个很好的想法,所以我们来静态编译bash shell然后将它拷贝到我们都可移动存储设备上.第2a步被替代为:
(compromised)# /mnt/cdrom/bash
必须让我们引起注意的是,当任何命令被健入系统时命令解释器都会将它们写入历史纪录文件.但我们不向修改本地系统的文件所有最好将历史纪录关掉.
2.3 Data collection for a live system - continued
第六步:物理内存镜象
数据收集的一个步骤是获取整个系统内存的一个拷贝.我们可以通过拷贝/dev/mem设备或者拷贝kcore文件来直接得到物理内存.kcore文件在linux系统中是用来纪录RAM中内容的.它是一个虚拟的文件,存在于/proc目录下.整个文件的大小和当前物理内存的大小是一样的,扩展了大约4KB.用kcore文件的好处是它是ELF格式的,所以可以用gdb进行调试.个gdb工具集是跟踪和分析小模块代码或内存的绝佳工具.在2.7节中我将为你展示如何在分析中使用这些工具的高级功能.但作为基础的分析我将使用更通用的工具比如grep,string(字符串),hex(十六进制)编辑器.
另外,为了能很好地分析整个物理内存我们必须了解一些关于操作系统的内存的页面方面的知识------相对于物理地址的虚拟地址表的数据结构.我们必须知道物理内存包含着虚拟内存的页面.在页面表(块表)中我们可以找到关于被写入物理内存的页面的索引(在intel处理器中为4KB每页).
如前面所提及的,我们在利用软件工具获取易失性存储器的内容时会很自然地改变内存的内容.更严重的是,我们会重写可能存在入侵证据.
下面的例子我使用了/proc虚拟文件系统来获得内存镜像
(remote)#nc -l -p port > kcore_compromised
(compromised)#/mnt/cdrom/dd < /proc/kcore | /mnt/cdrom/nc (remote) port
(remote)#md5sum kcore_compromised > kcore_compromised.md5
其实,在拷贝整个系统内存时我们既拷贝了已经分配出去的内存也拷贝了未分配的内存,原因是我们只是复制了这个物理内存的镜象.在第九步中我们将尝试用/proc虚拟文件系统去复制一个确切进程所使用的内存.我必须说明当是当我们用/proc时其实我们会改变swap交换分区里的内容.为了能复制那些可疑的进程我们将强制主存的一些页面对于swap交换分区是可读的,其他页面则是可写的.另一个重要的方面是,我们只拷贝进程已经分配的内存空间.
第七步:被载入操作系统内存的模块列表
我们要保证所收集到的数据是完整的,并且netstat和lsof命令的结果不能在内核级上被修改.所以我们必须检测当前那些模块被加载到内存中了.
(remote)# nc -l -p port > lkms_compromised
(compromised)#/mnt/cdrom/cat /proc/modules | /mnt/cdrom/nc (remote) port
(remote)# nc -l -p port > lkms_compromised.md5
(compromised)# /mnt/cdrom/md5sum /proc/modules | /mnt/cdrom/nc (remote) port
不幸的是,一些恶意的进程根本不会被列出来(译者注:恶意进程本身有保护机制).为了检测从/proc/modules加载的模块的信息,我将用在最近Phrack Magazine的文章"Finding hidden kernel modules (the extrem way)".中的方法.检测模块hunter.o检查一系列被加载到内核中的模块.
(compromised)#/mnt/cdrom/insmod -f /mnt/cdrom/hunter.o
我用了-f参数,它表示强制加载hunter.o模块因为版本不匹配.版本不匹配是很hunter.o的编译上的系统内核和当前使用系统的内核不一致所导致,避免这种情况的最好办法是在每个你使用的系统上重新编译代码.如果你知道你所操作的非安全系统的内核版本,你可以从www.kernel.org下载源代码和喜爱相应的头文件以重新编译和连接以产生和你系统 版本匹配的hunter.o.
(remote)#nc -l -p port > modules_hunter_compromised
(compromised)#/mnt/cdrom/cat /proc/showmodules && /mnt/cdrom/dmesg | /mnt/cdrom/nc (remote) port
(remote)#md5sum modules_hunter_compromised > modules_hunter_compromised.md5
我们现在可以比较结果了.还必须注意模块的大小,有时一些恶意代码会插入合法的文件中.(译者注:这样就会改变文件的大小)
最后一件事是我们拷贝内核模块输出的符号数据.有时脆弱的基于LKM的rootkit会输出自己的符号数据.通过分析ksyms文件系统我们可以检测到在系统中是否存在入侵者.
(remote)#nc -l -p port > ksyms_compromised
(compromised)#/mnt/cdrom/cat /proc/ksyms | /mnt/cdrom/nc (remote) port
(remote)# nc -l -p port > ksyms_compromised.md5
(compromised)#/mnt/cdrom/md5sum /proc/ksyms | /mnt/cdrom/nc (remote) port
我们可以用其他工具(比如kstat或kern_check)模块来检测恶意模块,但不幸的是所有的这些工具都使用System.map这个文件.这个文件由系统产生和编辑.如果管理员没有复制这个文件并且没有建立校检和的话我们不应该信任存储在那里的系统所调用的地址.即使当系统所调用的地址是正确的,入侵者也可以使用惯用的伎俩将恶意模块隐藏在内核内存.作为此种情况的例子,adore-ng工具就可以替换已经存在提供系统目录列表的常驻内存管理器.
如果我们可以确保在System.map和ksyms文件中的系统调用地址不会被修改并且确认系统调用地址表中的调用地址没有被入侵者所改变.详情请看2.7节.
第八步:活动进程列表
所有的进程、开放的端口和文件的信息可以用lsof工具来收集.当然,其中的要求是在内存中不能有任何基于LKM的工具存在.在下一步中我们将拷贝可疑的进程.通过拷贝具体的进程我们可以很容易从系统的内存中区分出恶意数据.你应该可以回忆起来,我们在第六步已经收集所有的内存数据(kcore or /dev/mem)
(remote)#nc -l -p port > lsof_compromised
(compromised)#/mnt/cdrom/lsof -n -P -l | /mnt/cdrom/nc (remote) port
(remote)#md5sum lsof_compromised > lsof_compromised.md5
现在,我们来分析从lsof工具得来的结果.如果你发现任何活动进程是可疑的,那么现在就是一个很好的时机去拷贝它们.如果我们发现lsof工具在初始化时就被入侵者检测到,而此时我们看到结果很明显不准确.即使这样我们依然有机会恢复结果.我将在下一步向你展示任何操作.
我列举几个可疑的进程作为例子:
○正在监听非常规定TCP/UDP端口或开放的原始socket的进程
○一个和远程主机有活动连接的进程
○一个在被删除到程序又开始运行
○一个文件被一个进程打开被删除了(比如:一个日志文件)
○一个非常奇怪的进程名字
○一个进程被一个不存在的用户初始化或者被一个没有权限的用户初始化
第九步:可以进程的当前连接
我用pcat工具拷贝整个被进程所分配的内存.
(remote)#nc -l -p port > proc_id_compromised
(compromised)#/mnt/cdrom/pcat proc_id | /mnt/cdrom/nc (remote) port
(remote)#md5 proc_ip_compromised > proc_ip_compromised.md5
其实,我可以只拷贝进程的特定数据.更多的信息请看下一节.
第十步:非安全系统的有用信息
下一步就是我们来收集大量非安全系统的有用信息.
这些信息需要适当的描述并且准备一个系统文件的拷贝.紧记,所有的结果都必须发送到远程主机上.在表2中我已经列举了所有必须在非安全系统是运行的命令.
Table 3: Useful information about the compromised host
Command
Description
/mnt/cdrom/cat /proc/version
Version of the operating system
/mnt/cdrom/cat /proc/sys/kernel/name
Host name
/mnt/cdrom/cat /proc/sys/kernel/domainame
Domain name
/mnt/cdrom/cat /proc/cpuinfo
Information about hardware
/mnt/cdrom/cat /proc/swaps
All swap partitions
mnt/cdrom/cat /proc/partitions
All local file systems
/mnt/cdrom/cat /proc/self/mounts
Mounted file systems
mnt/cdrom/cat /proc/uptime
Uptime
第十一步:当前时间
最后一步是关于当前时间信息的收集.
(remote)#nc -l -p port > end_time
(compromised)# /mnt/cdrom/date | /mnt/cdrom/nc (remote) port
现在我们到了可以关闭系统非安全系统的时候了.请注意,不要用任何shutdown或init命令来关闭系统.而要采取直接拔掉电缆或UPS电源的方法强行关闭.
2.4 文件系统镜象
在关闭非安全系统前我们是应该建立整个文件系统拷贝核交换分区的拷贝的,但我们并没有这么做.我建议在关闭系统之后在完成这件事.这样我们可以确保非安全系统的内存不在被修改.再需要提及的是,交换分区的内容在我们的获取进程进行时依然会被修改或被重写.
下一步是将可启动设备挂接到系统上并从中运行操作系统.我们可以使用Linux的默认设置不加载本地文件系统.现在我们可以从这一点上拷贝整个本地分区或整个硬盘.
2.5 基础数据分析
现在让我们再考虑一下在第八步中进程,在那里我们使用了lsof工具.让我们对此情况再深入地分析一下并考虑两个例子:
例1:一个被删除的程序开辟了进程
在这种情况下,被已经初始化了进程的Linux文件仍然在内存中被分配空间.为了恢复文件我们必须知道由程序建立的进程的ID.在/proc/目录下可以找到这个可执行文件.这是个被删除文件副本.我们将它发送到远程主机上.
当我们恢复了文件我们可以从得到一些信息.有两个操作可选:反汇编进行静态分析,或者在可控制的环境下运行这个未知文件进行动态分析.
例2.被活动的进程打开的文件被删除了(如日志文件)
lsof返回的部分结果如下:
smbd 3137 root rtd DIR 8,1 4096 2 /
smbd 3137 root txt REG 8,1 672527 92030 /usr/bin/smbd -D
smbd 3137 root mem REG 8,1 485171 44656 /lib/ld-2.2.4.so
...
smbd 3137 root 16u IPv4 976 TCP *:https (LISTEN)
smbd 3137 root 17u IPv4 977 TCP *:http (LISTEN)
...
smbd 3137 root 20w REG 8,1 253 46934 /var/log/httpd/access_log (deleted)
...
为了恢复此文件还要从/proc/3137目录下列出软驱子目录(文件描述符)的内容
(remote)# nc -l -p port > ls_from_proc_3137
(compromised)# /mnt/cdrom/ls -la /proc/3137/fd/ | /mnt/cdrom/nc (remote) port
(remote)# more ls_from_proc_3137
l-wx------ 1 root root 64 Aug 10 21:03 12 -> /var/log/httpd/access_log (deleted)
...
我们可以看到,第一个文件描述符被删除了.我们要做到事就是将此文件用netcat工具传送到远程主机上.
(remote)# nc -l -p port > deleted_access_log
(compromised)# /mnt/cdrom/cat /proc/3137/fd/1 | /mnt/cdrom/nc (remote) port
这不是恢复此文件的唯一方法.我们也可以在离线分析的时候寻找未分配的节点和数据块来恢复.
2.6 关键字查找
在本文的第一部分我向你展示了一种分析进程和内存镜像的一种方法.记住,整个过程是在远程主机上完成的,并且此时内存镜像已经完成.整个方法的最大好处是我我们不用知道低级语言的使用.
我将用下面的简单的工具去寻找入侵者的信号.
●strings
●less
●grep
我将用字符串工具收集所有在镜像文件中的可打印字符.工具的默认设置使我们可以显示所有的可打印字符,这些字符至少有四个.我们用-t参数使其从文件的开始进行统计.
$ strings -t d kcore > kcore_strings
$ md5sum kcore_strings > kcore_strings.md5
在最初的分析中grep工具和规则的操作很重要.几分钟后我们就可以找到入侵者明显的痕迹.必须考虑的是我们要寻找什么类型的数据----比如是否在寻找入侵者使用过的命令,IP地址,密码或者甚至很明显的恶意代码的明文?
下面有一些要寻找的关键字的例示.我们用这些关键字在kocre_string文件中找出入侵者的明显痕迹.
● host name or prefix of the compromised system
$ grep "root@manhunt" kcore_strings
$ grep "]#" kcore_strings
11921096 [root@manhunt]#
16643784 [root@manhunt root]#
30692969 ]#]#
上面的结果是我们列举的一些字符串的偏移.下一步是用文本编辑器打开文件并跳到接近于直接偏移地址的地方.如果幸运的话我们可以找到以前使用过的命令.但是我们也必须意识到的是物理内存中虚拟内存的页面和交换分区是被无规则的写入的,所以我们的结论也许是完全不正确的.
$ less kcore_strings
/11921096
11921096 [root@manhunt]#
11921192 /usr/bin/perl
11921288 perl apache_mod_exploit.pl
...
上面的例子展示了非安全系统曾被键入的一些命令.
● 文件和目录名
$ grep -e "\/proc\/" -e "\/bin\/" -e "\/bin\/.*?sh" kcore_strings
$ grep -e "ftp" -e "root" kcore_strings
$ grep -e "rm -" kcore_strings
$ grep -e ".tgz" kcore_strings
● IP地址和域名
$ grep -e "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" kcore_strings
$ grep -e "\.pl" kcore_strings
2.7 高级定性分析
在最后一节我来展示怎样分析用gdb工具来分析kcore文件的副本.开始让我描述一下怎样检测系统调用在系统调用表中的地址值是否合适.改变系统调用地址是在系统中安装了LKM工具的最简单的方法.下一个例子我将向你展示如何列举出所有在非安全性它是曾经运行过的进程.这个例子可以和第八步和第九步中所得结果进行比较.
为了分析我们必须了做到以下要求::
● 以ELF格式纪录的内存镜像
● 一个内核镜像的副本(我们必须挂载非安全系统的景象并且从/boot目录拷贝一个vmlinux文件
● 输出列表(System.map文件在/boot目录下或者我们可以利用ksyms文件---参看第七步)
● 所使用的系统的源代码
下一步是运行gdb工具:
#gdb vmlinux kcore
GNU gdb Red Hat Linux (5.1.90CVS-5)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
warning: core file may not match specified executable file.
Core was generated by `ro root=/dev/sda2 hdc=ide-scsi'.
#0 0x00000000 in ?? ()
(gdb)
现在我们开始分析.
例1:系统调用地址核实
第一步是找出系统调用表的调用地址值.几乎所有的Linux操作系统都会将此信息输出,可以在Symbol.map中找到这些信息.
# cat Symbol.map | grep sys_call_table
c02c209c D sys_call_table
现在,我们可以列举地址调用表的所有入口地址.每个入口地址就是系统调用的地址.
为了得到正确的入口地址参数我们要察看内核源代码中的entry.s文件.这个文件记录了系统调用的顺序.
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open) /* 5 */
.long SYMBOL_NAME(sys_close)
...
在sys_call_table中也有同样的顺序.下面列举十个开始的系统调用地址值:
(gdb) x/10x 0xc02c209c
0xc02c209c <sys_call_table>: 0xc01217c0 0xc011ac50 0xc0107510 0xc0138d50
0xc02c20ac <sys_call_table+16>: 0xc0138e50 0xc0138880 0xc01389b0 0xc011b010
0xc02c20bc <sys_call_table+32>: 0xc0138930 0xc01445c0
0xc01217c0是sys_ni_call系统调用地址, 0xc011ac50是sys_exit地址调用地址.
如果信任来自Syetem.map文件或者ksyms文件的地址那么我们可以比较所有的地制值.当然入侵者不会改变所有的系统观调用地址值.比如sys_read,sys_getdents,sys_write这些普通进程.
例2.活动进程列表
为了能得到整个运行进程的列表我们必须找出init_task_union结构体.这个结构体指明了第一个进程的描述符,这个被称为"process 0" 或者 "swapper".
# cat Symbol.map | grep init_task_union
c02da000 D init_task_union
下一步是如何找出整个结构体.其实init_task struct可以在源代码中的sched.h头文件中被找到,同时task_struct这个结构体也可以在这个头文件中被找到.
#define INIT_TASK(tsk)
{
state: 0,
flags: 0,
sigpending: 0,
addr_limit: KERNEL_DS,
exec_domain: &default_exec_domain,
...
run_list: LIST_HEAD_INIT(tsk.run_list),
time_slice: HZ,
next_task: &tsk,
prev_task: &tsk,
p_opptr: &tsk,
...
对于我们进程描述符中的重要部分是prev_task和next_task..它们可以帮助我们建立进程列表. next_task指向进程的下一个进程的描述符, prev_task指向被插入的最后一个进程的描述符.
在下面,我列举了进程描述符的片断,这些是在地址: 0xc514c000处被找到的.
(gdb) x/180x 0xc514c000
...
0xc514c040: 0x00000000 0xffffffff 0x00000004 0xc1be0000
0xc514c050: 0xc4dac000 0xc5ea8e40 0xc5ea8e40 0xc02c56d4
...
0xc514c070: 0x00000001 0x00001ace 0x00001ace 0x00000000
...
0xc514c230: 0xffffffff 0xffffffff 0x61620000 0x00006873
0xc514c240: 0x00007974 0x00000000 0x00000000 0x00000000
...
说明:
0xc1be0000 is the next_task
0xc4dac000 is the prev_task
0x00001ace is the process PID = 6268 in decimal format
0x61620000 and 0x00006873 is the name of process (the comm table in the struct) = "bash" in this particular example
打印出进程的下一个描述符我们这么做:
(gdb) x/180x 0xc1be0000
从每个进程描述符的信息我们可以建立所有活动进程的列表.
3. 总结
本文的目的是介绍一些数据收集,基本和高级分析的方法.特别地,我将精力聚焦在我们强行关闭系统后丢失数据上.这个部分的定性分析何事后措施很难完成,我们必须对每个步骤都非常小心并且要保证纪录(文档)每个细节.我们也可以看到用软件收集过程的好处和弊端.在最后我向你介绍了如何在Linux系统中进行收集操作,其实你完全可以将这些步骤移植到其他操作系统上.未来将有一篇指导你如何在Windows操作系统中收集入侵数据的文章.
参考:
Adore-ng rootkit, http://stealth.7350.org/rootkits/ Alessandro Rubini, Jonathan Corbet. Linux Device Drivers, 2nd Edition. O'Reilly; 2001. Dan Farmer, Wietse Venema. Column series for the Doctor Dobb's Journal. http://www.porcupine.org/forensics/column.html. Daniel P. Bovet, Marco Cesati. Understanding the Linux Kernel, 2nd Edition. O'Reilly; 2002. Kernel source code. http://www.kernel.org/ Linux manual pages. National Institute of Standards and Technology. Computer Security Incident Handling Guide. http://csrc.nist.gov/. PHRACK #61. Finding hidden kernel modules (the extrem way) by madsys. http://csrc.nist.gov/. RFC 3227. Guidelines for Evidence Collection and Archiving. Smith Fred, Bace Rebecca. A guide to forensic testimony. Addison Wesley; 2003. Symantec Corporation. CodeRed Worm. http://securityresponse.symantec.com/. The Honeynet Project. Scan 29. http://www.honeynet.org/ The SANS Institute. Incident Handling step by step. http://www.sans.org/About the author
View more articles by Mariusz Burdach on SecurityFocus.
Credits
Thanks to Kelly Martin and Dan Hanson for their suggestions, careful reviewing and help.
本文地址:http://com.8s8s.com/it/it36405.htm