简介
eBPF在恶意活动检测方面仍是未知领域。随着威胁的不断出现,我们需要采取主动。参与者将了解到不同类型的rootkits,它们的攻击流程,它们的运作方式以及如何检测它们。最后,他们将学习如何使用Tracee,这是一个利用eBPF来检测这些威胁并在攻击者面前获得优势的开源工具。
本篇议题是由Aqua Security实验室的Asaf Eitani 和Idan Revivo两位研究员带来
议题分析
基础知识
讨论rootkit需要知道的基础知识
用户空间与内核空间
通过系统调用交互
hook
追踪rootkit可能用到的技术,不改变两段代码逻辑的前提下,在两段逻辑之间插入一段逻辑
rootkit追踪技术
选择追踪rootkit的手段之前,需要知道,他们需要具备如下特征
- 低开销,别影响性能
- 隐蔽,不能被攻击者察觉
- 难以逃避,攻击者无法反制我们
- 完整的系统可见性,获取更多的信息以至于让我们确认现在安全
- 安全,物理上的,别把系统搞崩
目前方法的优劣:
eBPF
架构
eBPF,全称为扩展伯克利包过滤器(Extended Berkeley Packet Filter),是一种在Linux内核中运行的轻量级虚拟机。eBPF的主要优点是它能够在不更改内核源代码或加载内核模块的情况下,动态地在内核中插入程序。这些程序可以用于收集性能数据、跟踪系统调用、监视网络流量等。eBPF程序通常使用C语言编写,然后通过LLVM编译器编译成BPF字节码,最后由内核的BPF虚拟机执行。
用户空间将eBPF字节码传入内核的verifier模块进行运行前检查,通过之后送到BPF沙箱中允许,这时可以使用内核提供的各个功能的探测器(hook的其他模块)来进行操作。结果会返回给用户空间。用户空间也可以通过maps和内核交互。
Tracee - 一个运行时安全检查工具
演讲者所在组织开发的开源工具,使用eBPF来完成堆操作系统中rootKit的追踪。各种好,拥有自己的规则引擎,可以自己编写规则动态检测,源码如下:
GitHub - aquasecurity/tracee: Linux Runtime Security and Forensics using eBPF
RootKit种类与各个阶段的攻防
Rootkit是一种恶意软件,它的主要目标是在系统中隐藏自己的存在,同时保持对系统的最高级别访问权限,也就是"root"权限。Rootkit的主要特点是它的隐蔽性和持久性。一旦Rootkit安装在系统中,它就会尽可能地隐藏自己的存在,包括隐藏文件、进程和网络连接,甚至修改系统日志来消除任何被发现的痕迹。同时,由于Rootkit具有最高级别的系统访问权限,它可以完全控制系统,包括安装其他恶意软件、窃取敏感数据、监视用户活动等。
Rootkit可以在操作系统的不同层级安装,包括内核级(Kernel-level)和用户级(User-level)。内核级Rootkit可以直接修改操作系统内核的代码或数据结构,从而获得更高级别的控制和更强的隐藏能力。用户级Rootkit则通常通过修改系统的用户模式服务和应用程序来实现其目标。
LD_PRELOAD RootKit
LD_PRELOAD是Linux系统中的一个环境变量,它可以用来改变程序运行时的链接行为。具体来说,当你设置了LD_PRELOAD环境变量后,系统在启动一个程序时,会先加载LD_PRELOAD指定的共享库,然后再加载其他的库。这样,LD_PRELOAD指定的库就可以覆盖其他库中的函数,改变程序的行为。
LD_PRELOAD Rootkit就是利用这个机制来实现其恶意行为的。它通常会提供一个自定义的共享库,这个库中包含了一些系统函数的"伪装"版本。当LD_PRELOAD设置为这个库时,系统在运行程序时就会使用这些"伪装"的函数,而不是原本的系统函数。这样,Rootkit就可以在这些函数中插入恶意代码,实现例如隐藏文件、修改系统调用返回结果等行为。
例如,一个常见的LD_PRELOAD Rootkit可能会提供一个"伪装"的readdir
函数。readdir
函数是用来读取目录内容的,Rootkit可以在这个函数中过滤掉一些文件,使得这些文件在ls命令等操作中不可见。
总的来说,LD_PRELOAD Rootkit是一种用户级别的Rootkit,它利用Linux的动态链接机制来实现其恶意行为。虽然它的能力没有内核级Rootkit强大,但是由于其实现相对简单,侵入性较低,因此在实际中也比较常见。
使用eBPF检测
创建一个事件,每次将共享对象加载到内存的时候,检查所有导出符号,如果与部分攻击符号有关,则会创建一个事件说明这个符号正在被加载进内存。这不是原始库的一部分。
大概意思应该是加载动态库的时候检测动态库,如果这里有一些符号使用了一些敏感可能被用于攻击的其他符号(还需要手动指定),则会输出事件。我感觉这个检测还是比较好绕过的。我可以不使用任何其他符号,直接提供一个覆盖掉libc中符号的方法,或者直接加载一个fakelibc。而且手动指定敏感符号就类似黑名单,众所周知黑名单不是一个很好的防御机制
Kernel Module RooKit
Kernel Module Rootkit是一种内核级别的rootkit,它通过在操作系统内核中插入或修改模块来实现其恶意行为。由于内核模块具有最高级别的系统访问权限,这种rootkit可以完全控制系统,并且具有很强的隐藏能力。
Kernel Module Rootkit通常会提供一个自定义的内核模块,这个模块中包含了一些系统函数的"伪装"版本。当这个模块被加载到内核时,它就可以覆盖内核中的函数,改变系统的行为。例如,rootkit可能会提供一个"伪装"的read
函数,这个函数在读取文件内容时,可以过滤掉一些数据,使得这些数据对用户不可见。
由于Kernel Module Rootkit的强大能力和高度隐蔽性,检测和移除这种rootkit是一项非常困难的任务。这通常需要专门的反rootkit工具,以及深入的系统知识和经验。
内核RootKit生效通常会有如下五个步骤,接下来也会从这5个步骤中依次突破。
检测内核RootKit - 在加载中
使用eBPF在do_init_module函数中添加钩子,监听该事件就可以得知加载到内核的内核模块,然后会将内核模块所在缓冲区数据传出,以供进一步分析这些内核模块发现rootkit
Kernel RootKit Hiding
内核中含有一个双链表链接所有内核模块结构体,遍历该双链表可以得到所有已经加载的内核模块,RootKit可以将自己的内核模块结构体从该双链表中摘除来隐藏自己:
检测试图隐藏自己的Rootkit
创建一个时间规律的事件,每次触发会检测这个链表,如果发现跟之前有些差异,则说明有人想要隐藏自己。
Kernel RootKit Hook
这一步是RootKit实际开始攻击的第一步,有三种常见的做法:
syscall table hooking
系统调用表是用户态切换到内核态的时候给内核的指令,内核会根据指令编号寻找对应的系统调用入口,这样存在一个表去记录指令编号和入口的对应关系。RootKit通过篡改系统调用表中某些系统调用的入口指针实现对该系统调用的hook和劫持。当触发该系统调用的时候会指向恶意RootKit指定的代码,他可以控制返回给用户什么内容。
检测对系统调用表的劫持
检查系统调用入口指针是否存在于s_text和e_text之间,这是绝大部分内核符号所在的地址范围。如果出现系统调用表指向该范围之外的区域(实际上指向一个模块),则有问题
file operations hooking
file_operations 是内核的一个结构体,由多种文件操作的回调函数组成,如read、write、lseek、mmap等等。在任意文件系统和文件类型中,可以通过该结构体制定文件允许的操作类型,即设置对应回调函数。如,ls命令会调用file_operations中的iterate_shared回调函数获取目录下的文件信息:
攻击者可以通过篡改file_operations中的特定回调函数来劫持该逻辑:
然后再这其中可以篡改一些返回内容,或增加自己的攻击代码。
Drovorub APT RooKit使用该种技术。
检测file_operations篡改
演讲者没有说怎么检测的。
Kernel RootKit Communication
貌似没啥好办法,因为攻击者有各种方式和自己的恶意软件交互,他看起来就想正常程序一样。
Command Execution
如果我们使用execve系统调用运行一个程序,那么他会调用security_bprm_check和sched_process_exec之后启动一个新进程去执行,而且大部分安全工具也是将自己hook到exec系统调用上。但并不只是从execve系统调用才可以产生新进程执行,call_usermodehelper函数也可以直接调用security_bprm_check来产生一个新进程:
这是我我们在用户空间看检测不到的,如果只是在execve系统调用进行监控,自然也是监控不到的。
开源恶意样本Reptile使用这种技术
检测命令执行
将上述几个点全部加上钩子列为监测点:
eBPF RootKit
使用eBPF的RootKit是最近新出现的,和传统RootKit差别如下:
eBPF Programs Load
他们使用eBPF模块启动,使用eBPF程序隐藏自身,同样拥有和恶意软件的交互渠道,也有自己的触发攻击载荷的方式。
检测eBPF RootKit
eBPF是通过eBPF系统调用传入一段eBPF字节码,可以通过监控eBPF系统调用的方式发现恶意样本,并将字节码传出以供分析。
eBPF Probes Placement
eBPF提供了很多接口可以影响其他进程/命令/系统调用等的执行,如:
- bfp_probe_write_user 向用户空间内存写数据。可以修改用户空间系统调用/函数的返回结果,比如修改read系统调用返回用户空间的返回结果,隐藏一些内容
- bpf_override_return 篡改返回值,这里特值return 的值,也就是rax,可以假装某个命令执行成功,来欺骗用户。
- bpf_skb_store_bytes & bpf_skb_pull_data 这两个可以篡改通信数据,修改skb数据包,可以将本机的数据泄露出去。
- bpf_send_signal 发送信号,可以杀死一个进程,比如杀死lsmod等防止用户发现自己。
检测eBPF探针
这种方法的缺点是,会在dmesg打印一些东西。会暴露自己:
或者检测其他eBPPF程序使用的探针(具体怎么检测没说):
Hiding eBPF RootKit
eBPF系统调用不只是传入eBPF程序,列出所有程序等功能也使用该系统调用,可以hook eBPF系统调用的方式隐藏加载的RootKit。
eBPF RootKit Communication Channel
使用共享内存来交互:
总结
本篇议题围绕RootKit的攻防技术展开讨论,列举了常见的LD_PRELOAD 用户层RootKit与各种内核层RootKit,如内核模块RootKit、eBPF RootKit。并分析了各种RootKit的工作流程,除此之外讲解了eBPF的主要功能和使用eBPF针对Rootkit的各种不同操作进行检测的技术。