前言
之前团队内技术交流时小伙伴分享的自建蜜罐深深的引起了我的兴趣,于是本人决定山寨一个类似的蜜罐把玩一番~但是摆在眼前的一个问题就是:
一些监控进程需要运行在蜜罐中,一但“请君入罐”后被黑客察觉到这些奇奇怪怪的进程,就十分尴尬了
所以当务之急是需要将这些监控进程隐藏起来,对Linux略知一二的楼主自然也就接受挑战啦。
准备工作
众所周知,Linux 操作系统天生自带一个虚拟分区 /proc,该分区下保存硬件信息、内核运行参数、系统状态信息等等,进程运行时的一些信息自然也就存在这个分区下。
如上图所示,系统里运行的每一个进程都会在 /proc 分区下新建一个以自己 pid 命名的目录,并将本进程的参数存到该目录下。在该目录下我们可以通过修改 cgroup 文件暴力的将进程绑定在某个 CPU 上,也可以通过修改 cpuset 文件来优化进程在 NUMA 架构系统上的运行效率,还可以通过修改 oom_adj 文件让系统 OOM 机制永远高抬贵手。而 ps、top 这类查看进程的命令恰恰也就是在 /proc 分区下收集信息。换句话说,如果让 ps、top 命令选择性失明,也就能达到我们隐藏进程的目的了。
初次尝试
网上关于 Linux 隐藏进程的方式有很多,比如:
1)强行将进程 pid 变为 0,这种方法存在破绽因此不予考虑。
2)还有一种简单的方法:系统启动时会依据 /etc/fstab 文件内容来挂载分区,在 proc 分区挂载参数中加入 hidepid=2 参数后,登陆系统的用户只能查看到当前用户启动的进程的信息。也就是说, tomcat 用户只能看到属于 tomcat 用户进程的信息。
这种方法也存在弊端,罐中的黑客只能看到有限的进程信息,可能就会产生会怀疑。而且如果再存在个最近流行的 tomcat 提权漏洞,那罐子的身份就会瞬间露馅。所以这种方式也不合适,究竟如何才能将我们的问题解决在系统最底层呢?
“肝”起来
对于 Linux 系统来说有着得天独厚的优势,我们可以从内核解决一切问题。可是楼主的 C语言 实在是捉急,不由得又开始在互联网上寻找巨人的肩膀。一篇相关的干货贴引起了我的注意 http://blog.csdn.net/billpig/article/details/6038330(文末有引用说明),这位作者的思路很明确,在内核中新增两个信号,当进程向内核发出 hide 信号时,内核将不会为该进程在 /proc 目录下生成对应的目录,从而也就从底层铲除了进程的信息,即使黑客获得了 root 权限也无法通过常规手段察觉到蛛丝马迹。除此之外,新增的unhide信号作用恰好与 hide 信号相反。
通过查阅 crux 官方文档发现,Demo 中使用的内核版本为 2.6.15。在include/asm-i386/unistd.h 文件中定义新信号 294 和 295。
系统在接收到我们新定义的 294 和 295 两个信号之后需要调用对应的函数来做出相应的动作。在 kernel/sys.c 中我们实现 294 和 295 信号调用的函数。如下图所示,sys_hide 和 sys_unhide 两个函数主要功能是修改进程 hide 变量的值。
proc 相关内核代码位于 fs/proc/base.c 中,在进程相关结构体中新声明变量hide,通过发送信号来修改 hide 的值,最后在 base.c 文件 proc_pid_readdir 函数中将 hide 变量的值作为进程是否在 proc 文件系统中体现的依据。
除此之外还有些零碎的步骤,查看上文中的原帖地址即可,不再赘述。
踩坑
新内核编译完成后本以为可以愉快的开始玩耍了,结果新内核根本无法启动。通过各种修改启动参数发现,内核报错 “kernel too old” ,这就很尴尬了。再次站在巨人的肩膀上发现,确实是因为 kernel too old 。之前缺乏编译老旧内核的经验,现在才知道原来 gcc 调用的 libc 不是完全向下兼容的,我们可以通过
- file /lib/libc-*.*.so
来查看当前 libc 能够编译的最低版本的内核,如下图所示 libc-2.5.so 可以编译最低到 2.6.9 版本的内核,如果编译内核版本低于 2.6.9 的话就会产生 kernel too old 的报错导致内核无法启动。
综上,我们的 libc 版本不可太高,又因为 Demo 中修改的是 i386 架构的内核源码,所以我选择的实验环境为 CentOS 5.11 (32bit) 。
后续
至此,我们就可以成功安装并启动 2.6.15 版本内核了,然而事情并没有结束。系统供外部进程使用的信号仅有如下 64 个。
所以一般情况下我们的进程只能发出上图这 64 种信号,我们想要发出 294 和 295 号信号就必须借助于与系统关系更密切的 C语言 程序完成。
然后通过执行命令
- gcc hide.c -fPIC -shared -o hide.so
将 hide.c 编译为 hide.so 的动态链接库。
以后我们如果有进程需要隐藏的话,只需要在代码里调用 hide.so 中的 hide 函数即可,同样 unhide 函数是他的逆过程。如下图所示,在 Python 中 import ctypes ,使用它调用 hide.so 库即可实现当前 Python2.7 进程的隐藏与恢复。
至此,已经实现了内核级别的进程隐藏,可以愉快地做一些想做而又怕被别人发现的事情了…
Linux下编译内核
就跟Windows编译程序是一样的思路,Windows 下程序编译之后是一个可执行的文件,Linux下呢,编译内核的时候,一般也是从Git上把代码拖下来,是一个代码工程,编译后,生成一个对应格式的文件,这个时候该文件在Linux下 就跟其他文件一样,就是一个普通的文件。如果你想要把编译的这个内核在当前的系统中启动起来的话,由于当前系统中已经有一个正在运行的内核了,一个思路是你可以设置Linux一个变量(类似于Windows的环境变量),将该变量指向这个新编译好的内核文件,然后这个时候再重启当前Linux系统,系统启动时会读取那个变量的值,然后启动新的内核。