在如何改变进程名称 中提到了一些隐藏进程信息的方式,当时遗留有一个问题:不论哪种方式,exe文件仍然有原程序相关信息。
本文将总结两种"隐藏/proc/{pid}/exe文件"的方式,并浅析两种方式的隐藏效果。
两种方式包括:
现象
思路来源于基于Linux Namespaces 特性实现的消音
先来看一个现象:/proc/31867/exe
指向的可执行文件内容是一串文本。
难道文本内容可以被当做二进制文件执行吗?
答案当然是不行的。这里只是执行/tmp/sleep命令的方式比较特殊,如下:
[root@instance-fj5pftdp ~]# echo 111 > /tmp/sleep
[root@instance-fj5pftdp ~]# unshare -m bash -c "mount -t tmpfs xx /tmp/;cp /usr/bin/sleep /tmp/sleep;/tmp/sleep 1000" &
[1] 31865
[root@instance-fj5pftdp ~]# ps aux|grep sleep
root 31867 0.0 0.0 108052 612 pts/74 S 10:01 0:00 /tmp/sleep 1000
unshare -m
命令创建了一个新的"mount命名空间",然后在这个空间中挂载了tmpfs文件系统,最后执行sleep程序。
"mount命名空间"是什么?
"命名空间"是容器的核心原理之一。从效果上来看,"mount命名空间"可以让宿主机和容器中的"挂载信息"隔离。
比如在上面例子中,宿主机上执行mount
命令是是看不到"tmpfs文件系统被挂载在/tmp"目录下的。
[root@instance-fj5pftdp ~]# mount | grep xx
[root@instance-fj5pftdp ~]#
这种方式的对抗效果怎么样?
可以从两个场景来评估"对抗效果":
1.和"应急分析人员"对抗:应急人员是否能够轻易发现异常?
2.和"安全产品"对抗:安全产品是否能够轻易发现异常?安全产品的信息采集策略是否受此种方式影响?
从"应急场景"的角度来看,应急分析人员如果很清楚/proc/{PID}/exe
指向的文件不可信,可以通过 对比"可疑进程"和1号进程,验证mnt命名空间不同
[root@instance-fj5pftdp ~]# ll /proc/31867/ns/mnt
lrwxrwxrwx 1 root root 0 10月 1 23:42 /proc/31867/ns/mnt -> mnt:[4026533456]
[root@instance-fj5pftdp ~]# ll /proc/1/ns/mnt
lrwxrwxrwx 1 root root 0 10月 1 23:43 /proc/1/ns/mnt -> mnt:[4026531840] // 和 4026533456 不同
然后通过nsenter命令获取特定进程的挂载信息
[root@instance-fj5pftdp ~]# nsenter -m -t 31867 mount|grep /tmp
xx on /tmp type tmpfs (rw,relatime)
从"安全产品对抗"的角度来看,可以有以下结论:
1.hids可以监控mount系统调用来发现异常;不过不是所有的agent都会监控mount系统调用,比如字节hids[1]就没有。
2.hids上报文件时,如果是读取软链接指向的文件(比如案例中的/tmp/sleep,而不是/proc/31867/exe),就有可能上报错误的文件。
怎么修改软链接内容?
Linux进程伪装:动态修改/proc/self/exe[2] 文章中总结了两种方法:
1.execve函数
2.prctl系统调用
读者可直接阅读原文,这里我只记录一个自己看文章时的疑问:如果先创建线程A然后再调用execve,是否线程A还能继续执行?
答案是不行。
man 2 execve手册有说明:所有线程都被销毁
Note the following further points: * All threads other than the calling thread are destroyed during an execve(). Mutexes, condition variables, and other pthreads objects are not preserved.
自己测试了一下,可以验证"线程会被销毁"的结论。
测试代码[3],运行结果:
[root@vm10-50-0-18 ~]# gcc y.c -lpthread && ./a.out
hi
hi
hi
[root@vm10-50-0-18 root]#
这种方式的对抗效果怎么样?
原文第一种execve方式,我不太清楚使用场景:既然都execve来替换程序了,为什么不直接退出程序呢?
第二种"prctl系统调用"方式,对"应急响应人员"来说,迷惑性应该是很大的。对"hids产品"来说,prctl系统调用会是一个监控点。
比如字节hids对prctl系统调用是有监控的,可以参考elkeid的系统调用hook列表[4]。不过目前只有prctl系统调用的参数是PR_SET_NAME时才上报数据。这里用到的是PR_SET_MM参数,所以会绕过字节hids。
补充一种"修改软链接内容"的方式
当使用umount -l
卸载时,exe指向的路径也会修改,如下图
从上面分析可以看到,"/proc/{pid}/exe"文件相关信息似乎是不可信的:
/proc/{pid}/exe
对应文件的内容/proc/{pid}/exe
指向的文件个人感觉:这两种方式都有可能导致hids文件上报出现问题,也可能给"应急人员"的分析过程造成一定阻力。
其实这里存在一个缺陷导致隐藏效果打折:如果直接cat /proc/{pid}/exe
,就能可以读到源二进制程序内容。
那怎么让/proc/{pid}/exe
无法还原出二进制程序呢,这个问题留给读者思考。
本文提到的手段没有在真实的对抗中实践过,仅仅是我自己的研究,欢迎与我交流。
字节hids: https://github.com/bytedance/Elkeid/tree/main/driver
[2]Linux进程伪装:动态修改/proc/self/exe: https://xz.aliyun.com/t/10235
[3]测试代码: https://gist.github.com/leveryd/277852e2e741eb24b0b4bb8dc0220ef2
[4]elkeid的系统调用hook列表: https://github.com/bytedance/Elkeid/tree/main/driver