如何伪装进程信息

问题背景

如何改变进程名称 中提到了一些隐藏进程信息的方式,当时遗留有一个问题:不论哪种方式,exe文件仍然有原程序相关信息。

本文将总结两种”隐藏/proc/{pid}/exe文件”的方式,并浅析两种方式的隐藏效果。

两种方式包括:

  • mount命名空间
  • 修改软链接内容

mount命名空间

  • 现象

    思路来源于基于Linux Namespaces 特性实现的消音

    先来看一个现象:/proc/31867/exe指向的可执行文件内容是一串文本。

    图一

    难道文本内容可以被当做二进制文件执行吗?

    答案当然是不行的。这里只是执行/tmp/sleep命令的方式比较特殊,如下:

    1
    2
    3
    4
    5
    [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”目录下的。

    1
    2
    [root@instance-fj5pftdp ~]# mount | grep xx
    [root@instance-fj5pftdp ~]#
  • 这种方式的对抗效果怎么样?

    可以从两个场景来评估”对抗效果”:

    1.和”应急分析人员”对抗:应急人员是否能够轻易发现异常?

    2.和”安全产品”对抗:安全产品是否能够轻易发现异常?安全产品的上报策略是否受此种方式影响?

    从”应急场景”的角度来看,应急分析人员如果很清楚/proc/{PID}/exe指向的文件不可信,可以通过 对比”可疑进程”和1号进程,验证mnt命名空间不同

    1
    2
    3
    4
    [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命令获取特定进程的挂载信息

    1
    2
    [root@instance-fj5pftdp ~]# nsenter -m -t 31867 mount|grep /tmp
    xx on /tmp type tmpfs (rw,relatime)

    从”安全产品对抗”的角度来看,可以有以下结论:

    1.hids可以监控mount系统调用来发现异常;不过不是所有的agent都会监控mount系统调用,比如字节hids就没有。

    2.hids上报文件时,如果是读取软链接指向的文件(比如案例中的/tmp/sleep,而不是/proc/31867/exe),就有可能上报错误的文件。

修改软链接内容

  • 怎么修改软链接内容?

    Linux进程伪装:动态修改/proc/self/exe 文章中总结了两种方法:

    1.execve函数

    2.prctl系统调用

    读者可直接阅读原文,这里我只记录一个自己看文章时的疑问:如果先创建线程A然后再调用execve,是否线程A还能继续执行?

    答案是不行。

    man 2 execve手册有说明:所有线程都被销毁

    1
    2
    3
    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.

    自己测试了一下,可以验证”线程会被销毁”的结论。

    测试代码,运行结果:

    1
    2
    3
    4
    5
    [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列表。不过目前只有prctl系统调用的参数是PR_SET_NAME时才上报数据。这里用到的是PR_SET_MM参数,所以会绕过字节hids。

    图五

  • 补充一种”修改软链接内容”的方式

    当使用umount -l卸载时,exe指向的路径也会修改,如下图

    图六

总结

从上面分析可以看到,”/proc/{pid}/exe”文件相关信息似乎是不可信的:

  • “mount命名空间”会改变/proc/{pid}/exe对应文件的内容
  • 先知文章提到的两种方式会改变/proc/{pid}/exe指向的文件

个人感觉:这两种方式都有可能导致hids文件上报出现问题,也可能给”应急人员”的分析过程造成一定阻力。

但是如果直接cat /proc/{pid}/exe,就会发现仍然可以读到源二进制程序内容。

那怎么让/proc/{pid}/exe也无法还原出二进制程序呢,这个问题留给读者思考。

本文提到的手段没有在真实的对抗中实践过,仅仅是我自己的研究,欢迎与我交流。