Linux下基于内存分析的Rootkit检测方法

发布者:wyzsk
发布于:2020-08-19 17:34
作者: 路人甲 · 2015/01/23 9:52

0x00 引言


某Linux服务器发现异常现象如下图,确定被植入Rootkit,但运维人员使用常规Rootkit检测方法无效,对此情况我们还可以做什么?

enter image description here

图1 被植入Rootkit的Linux服务器

所有暗链的html文件ls均看不到。

使用ls -al 绝对路径,能看到,但无法删除。

这些暗链的uid和gid都很奇特 分别为 2511744398:4043361279 。

对任意文件执行chown 2511744398:4043361279 其表现会和暗链文件相同。

将硬盘nfs挂载到正常系统上,现象无任何变化。

0x01 Rootkit实现方式和检测方法


一般来说,Rootkit检测方式有以下几种:

1.  可信任Shell——使用静态编译的二进制文件:lsof、stat、strace、last、……
2.  检测工具和脚本:rkhunter, chkrootkit, OSSEC
3.  LiveCD——DEFT、Second Look、 Helix
4.  动态分析和调试:使用gdb根据System.map和vmlinuz image分析/proc/kcore
5.  直接调试裸设备:debugFS

在分析这几种检测方法的优劣之前,我们先通过图2了解一下Linux Rootkit的一般实现原理

enter image description here

图2 Linux中系统命令执行的一般流程

在Ring3层(用户空间)工作的系统命令/应用程序实现某些基础功能时会调用系统.so文件注1。而这些.so文件实现的基本功能,如文件读写则是通过读取Ring0层(内核空间)的Syscall Table注2(系统调用表)中相应Syscall(系统调用)作用到硬件,最终完成文件读写的。

那么如果中了Rootkit,这个流程会发生什么变化呢?下面通过图3来了解一下。

enter image description here

图3 Rootkit的一般执行流程

Rootkit篡改了Syscall Table中Syscall的内存地址,导致程序读取修改过的Syscall地址而执行了恶意的函数从而实现其特殊功能和目的。

上图仅仅是列举了一种典型的Rootkit工作流程,通过修改程序读取Syscall的不同环节可以产生不同类型的Rootkit,我们简单罗列一下。

Rootkit部分实现方式:

1.  拦截中断-重定向sys_call_table,修改IDT
2.  劫持系统调用-修改sys_call_table
3.  inline hook-修改sys_call,插入jmp指令

这部分不是本文的重点,不再赘述。了解了Rootkit实现原理,我们再回过来对比一下常规Rootkit检测方式的优劣。

对于使用静态编译的二进制文件的检测方式,如果Rootkit修改了Syscall,那么这种方法产生的输出也是不可靠的,我们无法看到任何被Rootkit隐藏的东西。

那么如果使用Rootkit检测工具呢,我们简单分析一下rkhunter的检测原理。

在rkhunter脚本文件中,scanrootkit函数部分代码如下:

enter image description here

图4 rkhunter中的scanrootkit函数

注:其安装脚本中定义了以下两个变量

#!bash
RKHTMPVAR="${RKHINST_SIG_DIR}"
RKHINST_SIG_DIR="${RKHINST_DB_DIR}/signatures"

enter image description here

图5 Signatures目录中的文件列表——Rootkit签名列表

从上面这段代码我们可以看出rkhunter扫描Rootkit调用了3个重要的变量:SCAN_FILES, SCAN_DIRS,SCAN_KSYMS,用于每种Rootkit的检查。

下面的四幅图分别是Adore和KBeast两个Rootkit检测的具体代码。

enter image description here

图6 rkhunter中经典Rootkit Adore的检测流程

enter image description here

图7 rkhunter中检测Adore的文件和目录的清单

enter image description here

图8 rkhunter中Rootkit KBeast的检测流程

enter image description here

图9 rkhunter中检测KBeast的文件和目录的清单

根据以上分析,我们可以看出rkhunter仅仅是检查已知Rootkit组件默认安装路径上是否存在相应文件,并比对文件签名(signature)。这种检测方式显然过于粗糙,对修改过的/新的Rootkit基本无能为力。

而另一款流行的Rootkit检测工具chkrootkit,其LKM Rootkit检测模块源文件为chkproc.c,最后更新日期为2006.1.11日。检测原理与rkhunter大致相似,也主要基于签名检测并将ps命令的输出同/proc目录作比对。在它的FAQ中Q2的回答也印证了我们的结论。

enter image description here

图10 chkrootkit的FAQ之Q2

分析了常见的Rootkit检测工具的实现原理,我们再看一下使用LiveCD检测这种方式有哪些局限性。

使用LiveCD意味着使用一纯净的光盘操作系统挂载原有存储对可疑文件做静态分析/逆向,以便了解Rootkit执行逻辑,依赖的so/ko文件有哪些,加载的配置文件是什么。那么,如果事先没有找到一些Rootkit的相关文件,直接对整个文件系统做逐一排查,无疑是一个繁冗的过程。而且,这种方式的使用前提是应急响应人员必须能物理接触服务器,这对托管在机房的环境很不方便。实际上,使用LiveCD在Rootkit清除或司法取证环节上更为常见,而不是其前置环节。

根据以上分析,我们简单总结一下Rootkit检测方式的效果,见下表.

Rootkit检测方式对比

检测方式 局限/缺陷
使用静态编译的二进制文件 工作在用户空间,对Ring0层的Rootkit无效。
工具rkhunter,chkrootkit 扫描已知Rootkit特征,比对文件指纹,检查/proc/modules,效果极为有限。
LiveCD:DEFT Rootkit活动进程和网络连接等无法看到,只能静态分析。
GDB动态分析调试 调试分析/proc/kcore,门槛略高,较复杂。不适合应急响应。
DebugFS裸设备直接读写 不依赖内核模块,繁琐复杂,仅适合实验室分析。

既然常规的Rootkit检测方法有这样那样的缺陷,那有没有更好的检测方式呢?

下面我们详细介绍一下基于内存分析Rootkit检测方法。

0x02 基于内存检测和分析Rootkit


Rootkit难以被检测,主要是因为其高度的隐匿特性,一般表现在进程、端口、内核模块和文件等方面的隐藏。但无论怎样隐藏,内存中一定有这些方面的蛛丝马迹,如果我们能正常dump物理内存,并通过debug symbols.和kernel`s data structure来解析内存文件,那么就可以对系统当时的活动状态有一个真实的“描绘”,再将其和直接在系统执行命令输出的“虚假”结果做对比,找出可疑的方面。下面简述一下部分原理。

1. 基于内存分析检测进程


在Linux系统中查看进程一般执行的是ps –aux命令,其实质是通过读取/proc/pid/来获取进程信息的。而在内核的task_struct注3(进程结构体)中,也同样包含进程pid、 创建时间、映像路径等信息。也就是说每个进程的相关信息都可以通过其对应task_struct内存地址获取。而且,每个task_struct通过next_task和prev_task串起成为一个双向链表,可通过for_each_task宏来遍历进程。基于这个原理我们可以先找到PID为0的init_task symobol(祖先进程)的内存地址,再进行遍历就能模拟出ps的效果。部分细节可参考下图。

enter image description here

图11 内核中的task_struct

此外,Linux内核中有一个东西叫PID Hash Chain,如图12所示,它是一个指针数组,每个元素指向一组pid的task_struct链表中的元素 ,能够让内核快速的根据pid找到对应的进程。所以分析pid_hash也能用来检测隐藏进程和获取相应进程信息,并且效率更高。

enter image description here

图12 内核中的PID Hash Chain

2. 基于内存分析Process Memory Maps(进程映射)


在task_struct中,mm_struct注4描述了一个进程的整个虚拟地址空间,进程映射主要存储在一个vm_area_struct的结构变量mm_rb注5和mmap 中,大致结构如下图所示

enter image description here

图13 mm_struct(内存描述符)的结构

每一个vm_area_struct节点详细记录了VMA(virtual memory area)的相关属性,比如vm_start(起始地址)、vm_end(结束地址)、vm_flags(访问权限)以及对应的vm_file(映射文件)。从内存中我们得到信息就相当于获得了/proc/


声明:该文观点仅代表作者本人,转载请注明来自看雪