Molet

Rootkit隐藏进程和端口检测

Molet 安全防护 2022-12-12 378浏览 0

一、引言

Rootkit是一种特殊的恶意软件,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息,比较多见到的是Rootkit一般都和木马、后门等其他恶意程序结合使用。

rootkit检测也成为主机安全一项重要功能,针对rootkit中最常见隐藏进程、端口检测,主要分为两种检测思路,一种基于内核内存分析,一种基于应用层分析。

基于内存分析Rootkit检测可参考Rootkit检测,该方案缺点是需要增加内核模块,风险高,检测效果相对较好。

本文介绍第二种方案,unhide在应用层发现隐藏进程、端口,该方案风险小,可集成到主机安全agent中。

Rootkit隐藏进程和端口检测

二、应用层隐藏进程检测

1. 进程隐藏和检测方式

进程隐藏两种方式:

  • 替换ps命令,在读取/proc/pid目录时,过滤掉需隐藏进程信息
  • 加载内核模块,通过拦截proc文件系统的回调函数,过滤掉需隐藏进程信息

检测核心思想:

通过libc系统函数盲测进程pid的存活状态,再根据ps结果对比差异,判断该pid是隐藏进程。

unhide提供如下19种检测方式,大致可分为四类:一类通过procfs下的进程目录信息,第二类通过系统调用函数, 第三类通过前两类组合方式,第四类通过爆力破解(不推荐)。

tab_test[TST_PROC].func=checkproc;
tab_test[TST_CHDIR].func=checkchdir;
tab_test[TST_OPENDIR].func=checkopendir;
tab_test[TST_READDIR].func=checkreaddir;
tab_test[TST_GETPRIO].func=checkgetpriority;
tab_test[TST_GETPGID].func=checkgetpgid;
tab_test[TST_GETSID].func=checkgetsid;
tab_test[TST_GETAFF].func=checksched_getaffinity;
tab_test[TST_GETPARM].func=checksched_getparam;
tab_test[TST_GETSCHED].func=checksched_getscheduler;
tab_test[TST_RR_INT].func=checksched_rr_get_interval;
tab_test[TST_KILL].func=checkkill;
tab_test[TST_NOPROCPS].func=checkallnoprocps;
tab_test[TST_BRUTE].func=brute;
tab_test[TST_REVERSE].func=checkallreverse;
tab_test[TST_QUICKONLY].func=checkallquick;
tab_test[TST_SYS_INFO].func=checksysinfo;
tab_test[TST_SYS_INFO2].func=checksysinfo2;
tab_test[TST_SYS_INFO3].func=checksysinfo3;

从名字上可以看出,unhide使用chdir,opendir,readdir,getpriority,getpgid,getsid,sched_getaffinity,kill,sysinfo等libc函数。

下面分别从三类中各选择一种检测方式分析。

2. procfs检测

抽取checkreaddir检测分析:

  • 遍历/proc目录, 子文件名如果是数字,代表是一个进程pid
  • 读取进程状态task目录(/proc/pid/task), 获取各子线程号
  • 通过ps命令查找是否存在该线程
  • 对比差异,不存在判断为隐藏进程
    procdir=opendir("/proc");
    dirproc=readdir(procdir));
    taskdir=opendir(task);
    dir=readdir(taskdir)
    checkps(procpids,PS_THREAD)
    

该类方法能检测出对第二种无效。

3. syscall检测

抽取checkgetsid检测分析:

max_pid通过读取 /proc/sys/kernel/pid_max获取:

  • 从1到max_pid遍历进程, 通过getsid返回值和错误码探测进程存活状态
  • 通过ps命令查找是否存在该进程
  • 再次通过getsid确认进程存活状态,防止在执行ps这时间内,进程退出了
  • 对比差异,不存在判断为隐藏进程
    ret=getsid(syspids)
    checkps(syspids,PS_PROC|PS_THREAD);
    ret=getsid(syspids)
    

该类方法都能检测上述两种隐藏进程方式。

4. compund检测

抽取checkallquick检测分析:

  • 从1到max_pid遍历进程
  • 通过kill返回值和错误码探测进程存活状态
  • 通过getpriority返回值和错误码探测进程存活状态
  • 通过getpgid返回值和错误码探测进程存活状态
  • 通过getsid返回值和错误码探测进程存活状态
  • 通过sched_getaffinity返回值和错误码探测进程存活状态
  • 通过sched_getparam返回值和错误码探测进程存活状态
  • 通过sched_getscheduler返回值和错误码探测进程存活状态
  • 通过sched_rr_get_interval返回值和错误码探测进程存活状态
  • 通过chdir,opendir读取进程目录(/proc/pid)
  • 通过ps命令查找是否存在该进程
  • 再次通过kill确认进程存活状态,防止在执行ps这时间内,进程退出
  • 对比差异,只有进程不存在(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,&param);
    ret=sched_getscheduler(syspids);
    statstatusproc=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盲测端口

1. tcp隐藏端口检测

  • 从1到65535遍历端口
  • 创建一个基于tcp协议SOCK_STREAM的socket
  • 通过bind返回值和错误码探测端口状态
  • 如果被占用,通过listen 错误码是EADDRINUSE确定端口占用
  • 通过ss或netstat命令过滤tcp协议,查看端口情况
  • 对比差异,确认该端口为隐藏端口
    socketsocket_desc=socket(AF_INET,SOCK_STREAM,0);
    bind(socket_desc,(structsockaddr*)&address,sizeof(address));
    listen(socket_desc,1);
    if(EADDRINUSE==errno){
    checkoneport(i,tcpcommand,TCP);
    }
    

2. udp隐藏端口检测

相比tcp, udp使用SOCK_DGRAM的socket, 缺少listen这步,其余检测步骤类似

socketsocket_desc=socket(AF_INET,SOCK_DGRAM,0);
bind(socket_desc,(structsockaddr*)&address,sizeof(address));
if(EADDRINUSE==errno){
checkoneport(u,udpcommand,UDP);
}

四、结论

本文提供的通过应用层方式检测rootkit中最常见的隐藏进程和端口,风险性小,可无缝集成到主机安全agent中。

继续浏览有关 安全 的文章
发表评论