简介
Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身以及指定的文件,进程和网络链接等信息,比较多见的是Rootkit一般都和木马,后门等其它恶意程序结合使用。Rootkit的三要素就是:隐藏、操纵、收集数据。
Rootkit的主要分类
Rootkit主要分为三类:应用级,内核级,硬件级
应用级:主要通过替换login、ps、ls、netstat等系统工具,或修改.rhosts等系统配置文件等实现隐藏及后门
硬件级:要指bios rootkit,可以在系统加载前获得控制权,通过向磁盘中写入文件,再由引导程序加载该文件重新获得控制权,也可以采用虚拟机技术,使整个操作系统运行在rootkit掌握之中;
内核级:目前最常见的,内核级rootkit又可分为lkm rootkit、非lkm rootkit。lkm rootkit主要基于lkm技术,通过系统提供的接口加载到内核空间,成为内核的一部分,进而通过hook系统调用等技术实现隐藏、后门功能。非lkm rootkit主要是指在系统不支持lkm机制时修改内核的一种方法,主要通过/dev/mem、/dev/kmem设备直接操作内存,从而对内核进行修改。
Rootkit的检测思路
Rootkit检测也成为主机安全一项重要功能,针对rootkit中最常见隐藏进程,端口,主要分为两种检测思路:
1)基于内核内存分析:缺点是需要增加内核模块,风险高;优点是检测效果最好
2)基于应用层分析:缺点是检测效果可能不如上一个;优点是在应用层发现隐藏进程,端口,风险小,可集成到主机安全agent中。
基于应用级的进程隐藏和检测方式
进程隐藏的方式上面已经说过一些,这边再说一下,主要是以下两种方式:
1)替换ps命令,在读取/proc/pid目录时,过滤掉需隐藏进程信息。
2)加载内核模块,通过拦截proc文件系统的回调函数,过滤掉需隐藏进程信息。
进程隐藏的检测方式,大致可分为四类:
通过libc系统函数盲测进程pid的存活状态,再根据ps结果对比差异,判断该pid是隐藏进程。
1)通过procfs下的进程目录信息
2)通过系统调用函数
3)通过前两类组合方式
4)通过暴力破解(不推荐)
下面分别从三类中各选择一种检测方式分析:
1)procfs检测:
基本检测流程:
1.遍历/proc目录,子文件名如果是数字,代表一个进程
2.读取进程状态task目录(/proc/pid/task),获取各子线程号
3.通过ps命令查找是否存在该线程
4.对比差异,不存在的判断为隐藏进程
1)procdir = opendir("/proc");
2)dirproc = readdir(procdir));
3)taskdir = opendir(task);
4) dir = readdir(taskdir)5) checkps(procpids,PS_THREAD)
该类方法能检测出第一种隐藏进程方式,对第二种无效。
2)syscall检测(OSSEC检测流程)
基本检测流程:
1)从1到max_pid遍历进程, 通过getsid返回值和错误码探测进程存活状态(python中,os.getsid这个命令调用不成功)
2)通过ps命令查找是否存在该进程
3)再次通过getsid确认进程存活状态,防止在执行ps这时间内,进程退出了
4)对比差异,不存在判断为隐藏进程
1)ret = getsid(syspids)
2)checkps(syspids,PS_PROC | PS_THREAD);
3)ret = getsid(syspids)
该类方法都能检测上述两种隐藏进程方式。
3)compund检测
基本检测流程:
1)从1到max_pid遍历进程
2) 通过kill返回值和错误码探测进程存活状态
3) 通过getpriority返回值和错误码探测进程存活状态
4) 通过getpgid返回值和错误码探测进程存活状态
5) 通过getsid返回值和错误码探测进程存活状态
6) 通过sched_getaffinity返回值和错误码探测进程存活状态
7) 通过sched_getparam返回值和错误码探测进程存活状态
8) 通过sched_getscheduler返回值和错误码探测进程存活状态
9) 通过sched_rr_get_interval返回值和错误码探测进程存活状态
10) 通过chdir,opendir读取进程目录(/proc/pid)
11) 通过ps命令查找是否存在该进程
12)再次通过kill确认进程存活状态,防止在执行ps这时间内,进程退出
13)对比差异,只有进程不存在(found=0)或者进程经过11项检测(found == 11)认为是正常的,其余都判断为隐藏进程
ret = kill(syspids, 0);
ret = getpriority(PRIO_PROCESS, syspids);
ret = getpgid(syspids);
ret = getsid(syspids);
ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
ret = sched_getparam(syspids, ¶m);
ret = sched_getscheduler(syspids);
statusproc = stat(directory, &buffer);
statusdir = chdir(directory);
dir_fd = opendir(directory) ;
checkps(syspids,PS_PROC | PS_THREAD)
ret = kill(syspids, 0);
if (found_killbefore == found_killafter) {
if ( ! ((found_killbefore == 0 && found == 0) ||
(found_killbefore == 1 && found == 11)) ) {
printbadpid(syspids);
}
应用层隐藏端口检测
核心思想:通过libc系统函数bind,listen盲测端口(65535个端口每个尝试去连接,消耗机器性能而且有点疯狂)
TCP隐藏端口检测:
1)从1到65535遍历端口
2) 创建一个基于tcp协议SOCK_STREAM的socket
3) 通过bind返回值和错误码探测端口状态
4) 如果被占用,通过listen 错误码是EADDRINUSE确定端口占用
5) 通过ss或netstat命令过滤tcp协议,查看端口情况
6)对比差异,确认该端口为隐藏端口
socket_desc=socket(AF_INET,SOCK_STREAM,0);
bind(socket_desc,(struct sockaddr *)&address,sizeof(address));
listen(socket_desc,1);
if(EADDRINUSE == errno) {
checkoneport(i, tcpcommand, TCP);
}
UDP隐藏端口检测:
相比tcp, udp使用SOCK_DGRAM的socket, 缺少listen这步,其余检测步骤类似
socket_desc=socket(AF_INET,SOCK_DGRAM,0);
bind(socket_desc,(struct sockaddr *)&address,sizeof(address));
if(EADDRINUSE == errno) {
checkoneport(u, udpcommand, UDP);
}
基于内核内存的检测方式
基于内存分析:这种方式需要增加内核模块,实现起来比较困难,而且要考虑稳定性问题,参考了其它文章的一些内容,如下:
Linux系统中存在用户态与内核态,当用户态的进程需要申请某些系统资源时便会发起系统调用。而内核态如何将系统的相关信息实时反馈给用户态呢,便是通过proc文件系统。如此便营造了一个相对隔离的环境。
那么proc文件系统是如何呈现我们平时最关心的进程/网络连接信息的呢?在/proc目录下存在着一些以数字命名的目录,这些数字便对应了系统中正在运行的进程的pid。自然,对应进程的一些相关信息便保存在/proc/{pid}目录下。比如/proc/{pid}/fd目录中就保存了进程打开的文件描述符。
系统会将tcp协议的连接信息保存在/proc/net/tcp文件中。其他网络协议的连接信息也均保存在/proc/net对应协议的文件中。需要特别指出的是,上图中socket文件名中括号里的数字与下图中inode列是存在对应关系的。
综上所述,像ps和netstat这类命令便是读取了proc文件系统中提供的数据,然后将数据格式化输出给用户的。出于隐藏指定进程/网络连接的目的,大部分Rootkit对写proc文件的回调函数做了些手脚,造成黑客指定的一些进程/网络连接的信息不会出现在proc文件系统中。于是,也就实现了进程/网络连接的隐藏。
### 探索
那么,如何发现那些被隐藏的进程/网络连接呢?首先是进程,这个其实并不复杂。系统给我们提供了一个方便的全局变量current。current永远指向当前正在运行的进程的task_struct数据结构,而进程的主要信息都包含在这个名为task_struct的巨大结构体中。当我们遍历到系统中所有进程task_struct结构体中的pid值,然后再去/proc目录下去寻找是否有对应的值。一但我们得到的pid未出现在ps的返回结果中,那么说明该进程被隐藏了。
如何发现被隐藏的网络连接呢?其实还是基于上一步进一步挖掘,因为网络IO请求都是由进程发起的,那么理论上来说我们肯定可以从进程一步步“捋”到socket。Linux一切皆文件的准则告诉我们,所谓网络IO其实就是读写socket文件,而每个socket结构体中都会有一个对应的inet_sock结构体,来记录IP协议下网络连接的四元组信息。那么我们把这些四元组再和netstat命令的返回结果比对一下便可知是否存在隐藏的网络信道了。
总结出来在Kernel2.6.32中,进程-文件-网络三者之间大致的关系如下图所示。
fdtable结构体的成员fd字符数组中存储的是file结构体,每当进程开启了一个文件描述符之后会自动在fd数组中新增一个对应的file结构体。通过获取file结构体中的f_path成员,我们可以获得对应的path结构体。系统内置的d_path函数可以返回path结构体对应文件的绝对路径信息。当d_path返回了一个“socket:[xxx]”的信息时,就说明这是个socket文件,该进程在试图进行网络IO。我们引用这个file结构体的private_data成员就可以获得这个socket文件对应的socket结构体了。最后通过内置的inet_sk()函数我们就可以找到对应socket文件的inet_sock结构体,我们需要的四元组便存在这个结构体中。
检测rootkit的两种工具(chkrootkit和rookit hunter)
1.chkrootkit:
已经停止更新,下面是他的检测方式:
搜索通用的ROOTKIT特征的字符串
对某种特定的rootkits,或者命令的特殊的感染特征进行检测
对某种特定的rootkits的生成的特定文件的检测
对程序的SUID位的设置进行检测
对ldsopreload的检测
查找可疑的log文件
查找可疑的php文件
检测.history文件
检测有无程序监听了一些可疑的端口
检测Linux可加载内核模块
检测有无隐藏进程
检测目录的软链接异常
检测网络接口的异常
检测用户的登录日志
检测上一次登录
检测可疑的没有tty记录的进程
2.rkhunter
1.下载:http://downloads.sourceforge.net/rkhunter/rkhunter-1.3.4.tar.gz?use_mirror=jaist
2.解压,使用
tar -zxvf rkhunter-1.3.4
cd rkhunter-1.3.4
./rkhunter -h
安装命令:./installer.sh --layout default --install
使用:rkhunter -C 检测
功能特性:
1. 基于指纹库思想的rootkit检测
1.1 建立了一个已知rootkit的默认目录名、文件名列表,通过默认文件名、目录名特征这个维度对rootkit进行检测
1.2 通过检查某个文件或目录是否存在来判断对应的rootkit病毒是否存在(FILESCAN)
1.3 在文件系统中搜索rootkit常用的文件名(SUSP_FILES_INFO)
1.4 检测是否有已知的sniffer(嗅探器)的日志文件存在(SNIFFER_FILES)
1.5 检测是否有已知的LKM Rootkit对应的.so文件存在(LKM_BADNAMES)
1.6 检查自从上次运行rkhunter以来,/etc/passwd的内容文件有没有变化
1.7 检查是否存在"逻辑rootkit",即通过减低指定系统配置或软件的安全配置来构建逻辑后门rootkit
2. 基于可疑字符串的指令劫持检测
2.1 检查某个"二进制系统命令"是否否存在某个字符串,如存在则有危险(STRINGSCAN)
2.2 依次用一些rootkit病毒的文件名或路径做参数调用strings命令,看其是否能正常输出
2.3 检查和动态链接库预加载相关的环境变量
1) LD_PRELOAD
2) LD_AOUT_PRELOAD
3) LD_ELF_PRELOAD
4) /etc/ld.so.preload
5) LD_LIBRARY_PATH
3. 基于可疑字符串的Linux启动文件恶意修改、劫持检测
3.1 检查某个"二进制启动文件"是否存在某个字符串,如存在则有危险(STRINGSCAN、RCLOCATIONS)
3.2 在系统启动文件中搜索特定的字符串,如果搜到,则说明有危险(RCLOCAL_STRINGS)
4. 基于异常文件目录属性特征的rootkit检测
1) 检查系统命令程序文件的ACL属性
1.1) 异常-s标志位
1.2) 权限开放过大(任意用户可执行)
1.3) 异常的隐藏标志位
1.4) 可疑文件修改时间
2) 检查敏感文件的HASH值是否和标准的一致(需要建立一个标准黄金基准列表),以确定这些系统命令是否被篡改过
5. 基于网络异常状态监控的rootkit检查
1) 检查指定端口上监听的程序是否在白名单中(正常的程序只会listen在指定的端口上)
2) 检查是否有白名单之外的程序在进行流程sniffer
6. 基于系统账户、组异常权限检查的rootkit监控
1) 检查/etc/passwd中是否有白名单之外的"超级用户(uid)",这是一种异常现象
2) 检查是否存在无密码账户
总结
Rootkit是一种特殊的恶意软件,常被用来隐藏端口,进程,文件等。
Rootkit检测可以从Rootkit特征以及行为等方面进行检测。
例如:
1.通过检测是否有隐藏的进程,端口等(分为两种方式:应用层分析以及内核层分析)
2.检测有无程序监听了一些可疑的端口
3.搜索通用的Rootkit特征的字符串
等等。
原文始发于微信公众号(信安路漫漫):Rootkit隐藏进程和端口检测
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论