Rootkit隐藏进程和端口检测

2019-03-28 68978人围观 ,发现 8 个不明物体 系统安全

*本文作者:zhouqiao,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

一、引言

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

例如:inetd或者login,为攻击者提供后门;隐藏攻击者的目录和进程的程序,ps、netstat等常见命令。

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

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

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

二、应用层隐藏进程检测

2.1 进程隐藏和检测方式

进程隐藏两种方式:

1) 替换ps命令,在读取/proc/pid目录时,过滤掉需隐藏进程信息

2)加载内核模块,通过拦截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.2 procfs检测

抽取checkreaddir检测分析:

1)遍历/proc目录, 子文件名如果是数字,代表是一个进程pid

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.3 syscall检测

抽取checkgetsid检测分析:

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

1)从1到max_pid遍历进程, 通过getsid返回值和错误码探测进程存活状态

2)通过ps命令查找是否存在该进程

3)再次通过getsid确认进程存活状态,防止在执行ps这时间内,进程退出了

4)对比差异,不存在判断为隐藏进程

1)ret = getsid(syspids)

2)checkps(syspids,PS_PROC | PS_THREAD);

3)ret = getsid(syspids)

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

2.4 compund检测

抽取checkallquick检测分析:

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, &param);

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盲测端口

3.1 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);

}

3.2 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);

}

四、结论

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

*本文作者:zhouqiao,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

发表评论

已有 8 条评论

  • 2222@163.com  2019-03-28 回复 2楼

    疯了, 65535个端口去尝试bind一下, 万一是端口允许多重绑定呢? 有谁会写个程序,65535个全去检测一下, 哪怕是安全类软件,也不会这样疯狂模式

    保护好你的权限,除了自己外,一般 不允许其它人操作shell, 除非有不得已的服务,不然都不以root权限运行,其它模块的, 操作他自己的机子, 各模块之间分开
    如果需要操作多人管理的,权限限制的死死的, 挂上yum自动更新的计划任务, 每个星期都在更新, 有了新内核发布了,更新完,马上重启掉, 这样,除非极端安全人员, 一般人拿不到root了, 打开rsyslog远程日志功能,尽可能的把任何操作记录记录日志, 这样公司内部员工, 不敢轻易动手脚

    rootkit应该做在kernel里, 要么弄在一些驱动或内核等mod模块里,或其它系统守护进程里, 比如有php.exe/elf 你在下了php的代码,写个后门进去
    当分析到特定数据时,触发反向连接,甚至反向连接也不要, 直接执行完特定数据包里包含的命令字符串, 这样系统内就不存在多出来的进程,
    一般反向连接,因为sysctl.conf中配置WAIT时间,大多有好几秒, 后台检测脚本检测netstat时,容易检测到,记录下来

  • 2222@163.com  2019-03-28 回复 3楼

    @ test 做技术的,就要冷血,比如,说不给权限就不给,没的通融,不然[灾难会无恨扩大的,特别是出事了,要你背锅的, 重启的话, 人工吧, 内核补丁是有热补的,有些补不上,只有重启才生效,所以,你半个月查一下是不是要重启就可以了, 或者说,你自己的工作机,发现有内核级补丁了,知道下面的服务器要重启了, 找个轻闲的时间,去人工重启一下, netstat 确定是非常慢的, 哪怕普通的小网站,调用一下netstat 都要一会儿, nginx有127.0.0.1:8080/status服务,可以查端口,不过一般都是低权的,没什么要查的 而真要有人反向连接了, 又不是24小时连在那里,而且netstat列表里,几千个记录里,只有一个是他, 把他找出来,不现实
    其它一些服务程序,本身也没有这些功能, 真要加后门, 如果是我,我直接以icmp方式通讯,不管是系统自带的,还是你安装的其它开源的工具包,都不会发现这种
    因为拿到root后, 运行的程序可以修改进程名的, 你在系统里,看到一个"[worker/1:1]" 99%,直接忽略了, 真要怀疑了,看了半天,网上还一搜,发现是内核的东西,非常放心了.. 所以,除非运行一些长时间程序,比如你mysql ,redis登录在上面,改东西, 改完了,还不退出, 管理员又正好上去排除,这才会被发现

    //@站长, 提交评论, 大机率的变成刷新页面, 打的内容全丢 这个什么时候修复啊, 你让我养成了,每次点提交前,先ctrl+C,好事后可以c+v回来的习惯

  • 2222@163.com  2019-03-28 回复 4楼

    多机时,.不要在A机上,写上一堆证书,互相信任,,然后可以自动登录B,C,D,E…..方便是方便了,但只要拿下一台机子,其它全部沦陷了, 要证书登录,一定要把私钥放在你自己电脑上,所有机子,都从你这里登录过去, A,B,C,D…之些互相不要访问, 或者非要访问,比如维护时,互拷个大文件什么的, 宁愿用密码方式
    密码可以pwgen 长度x 数量y 生成随机密码, 长度x建议>=32 , 更新怕出问题,你可以屏蔽掉一些历来升级不太靠谱的,或是已经极度安全的软件,
    ubuntu类用 "apt-mark hold 程序名" ,redhat/centos在/etc/yum.conf最后,添加 exclude=xxx 一行一个软件

    不说了, 这算常识, 我可不做网站这种低级东西,网管技术和工资都死低, 看门大爷的地位都比你高

  • ubuntu  (2级)  2019-03-29 回复 5楼

    做安全,不能见病不见人。你是依附于业务的,不能阻碍业务发展。可以修正,甚至提供更安全更好的方式,但不能打压。

取消
Loading...
css.php