摘要
BetterProFiler 是一个针对嵌入式 Linux 的进程动态跟踪器。BetterProfiler 可追踪进程与内核的整个生命周期,文件 IO,网络IO,内存操作,加密读写等。
BetterProfiler 相较于其他安全监控工具:
- BetterProfiler 运行在内核中,使用 eBPF 技术动态的附加到从内核的各项节点中,对系统进行全局性的观测和监控。
- BetterProfiler 相较于其他的跟踪工具,运行速度相当快,对系统负载极其小,可以在内存54M的嵌入式Linux中稳定运行6个月。
- BetterProfiler 监控范围覆盖内核的各项系统调用,如文件 IO,网络IO,内存操作,加密读写,甚至是底层驱动事件,且支持的调用还在增长中。
- BetterProfiler 提供标准的日志结构,提供统一的 API接口,可以作为核心接入现有安全产品,还可进行二次开发。
- BetterProfiler 使用了CO-RE 技术,完美解决了 BPF 可移植性差问题,且不依赖其它第三方库和,非常简洁,可以一次编写到处运行。
- BetterProfiler 部署非常简单,解压直接运行,不需要复杂配置。
BetterProfiler 可以帮助安全从业人员发现系统中已知和未知的威胁,也可以用来作为一种观测 Linux 内核高效手段,其观测点能覆盖到内核各个角落,是一种非入侵式的系统监测方法。可用于计算机取证,安全监控预警,ELF 逆向,病毒动态行为分析等。
概述
Linux 上始终没有一个好用的进程动态分析器,具体分析如下:
- 已有的跟踪工具对系统负载影响较大如基于 ptrace 的 strace 。应用程序每做一次系统调用都需要 ptrace 进行捕获, 获取到数据后再放行相应的系统调用,ptrace 需要做很多复杂的操作, 如果应用程序的系统调用很频繁, strace 就会对程序产生很大的影响。
- 以内核模块的方式加载进内核带有入侵性和破坏性如基于内核模块的sysdig。systemtap toolkit 则配置过于复杂,深入的分析需要在 systemtap 脚本中内嵌相关的代码才行,如果嵌入 c 代码,那么
systemtap
就很难保证代码的安全性。 - 基于 bpftrace 的 bcc 和 BetterProfiler 采取的相同的 eBPF 技术栈,bcc 能监控的地方从应用到内核,不同层面的工具应有尽有,bpftrace可以结合 kprobes/kretprobes/uprobes 封装成强大的动态分析器,但 bcc 使用仍旧复杂,需要开发者对内核跟踪技术有清晰了解,且 bcc 会在运行时编译 ebpf 代码,在运行时效率高,但在最开始对系统的负载较高,bcc 还有个重要缺陷就是对运行环境有严格需求,需要配置clang编译器和内核头文件,并不适用于嵌入式环境。
BetterProfiler 相较于其他 Linux进程跟踪办法:
- 它使用非常简单,因为我们替你已经封装好了大部分通用操作,用户只需要关注他们想要监控的行为逻辑。
- 它运行飞快,对系统几乎不造成负载,因为它使用的较新的eBPF内核观测技术,同时使用了JIT进一步加速代码在eBPF VM中的执行速度。
- 它不具有入侵性,相较于其他进程跟踪技术,它不是内核模块,不用注入进程上下文,不需要更改现有生产环境。
- 它覆盖了大部分内核系统调用,基本可以观测任何你想观测的东西,并且随着 eBPF 的发展,可观测点还在增加,只需要把感兴趣的观测点加入 BetterProfiler 中就行。
- 它容易部署,提及非常小,所有组件都自包含在单一可执行文件里,27M的体积可以放入路由器,光猫,机顶盒,Android 手机中。
设计与实现
内核从操作系统发展的历史上看就具有监督和控制整个系统的特权,所以理论上内核一直是实现检测追踪、安全模块,网络封包处理的理想场所。 内核是一个基于事件的系统,这意味着所有的工 作都可以用事件来描述和执行,打开文件是一种事件,执行一个 CPU指令是一个事件,接收网络数据包是一个事件。
简单来讲 BetterProfiler 和核心逻辑就是使用 eBPF 技术来监控内核中所触发事件,其中参考了bpftrace 与 bcc 的设计,在事件触发时追踪事件的来源,并执行开发者编写的代码。
我们将从以下几个方面讨论 BetterProfiler 的设计与实现:
- 以非入侵的形式载入内核态
- 覆盖大部分可监控区域
- 使用 JIT 加速指令运行
- 抵御恶意操纵
以非入侵的形式载入内核态
许多恶意软件分析方案都是基于内核模块的,从大公司私有的动态分析方案到开源的如 ftrace与 systemtap 都与内核有着强绑定关系,这样做的好处是可以覆盖比较大的可检测范围,缺点也显而易见就是一个字 「 危 」。内核由于其处于操作系统核心地位,对稳定性和安全性有高要求,类似空指针,偏移量,在错误的地址写入数据等问题都会使得内核崩溃,在控制台打印 kernel panic( 内核恐慌 ),此时整个系统旧歇菜了。这也就是 内核恐慌 为何会系统开发者口中的段子,因为在编写内核模块及内核子系统时,不正确的操作内核导致内核崩溃是基本上是必然事件。
BetterProfiler 不是内核模块,而是通过 eBPF 虚拟机载入内核,这是一种高级的虚拟机类似于 Java 虚拟机。为的就是在在隔离的环境中运行指令代码,eBPF虚拟机 使用 eBPF 验证器来确保BetterProfiler 的 eBPF 指令在内核中安全运行。 BPF 验证器能阻止可能使内核崩渍的代码。如果代码是安全的,此时 BetterProfiler 的 eBPF 指令程序将被加载到内核中。
这样做还有一个好处就是以非内核模块的形式载入内核中,相较于直接插入内核模块的入侵行为,安全性直接拉满,内核再也不会因为错误的代码产生内核恐慌现象。
其某些BetterProfiler 支持以非 Root 权限载入内核中,访问某一部内核数据结构的子集,在实际环境中,这些子集已经满足日常监控需求。
覆盖大部分可监控区域
硬件与内核态与用户态的关系大致可以描述为如下三层:
- 进程在用户空间(User Space)中,进程(Process)通过系统调用如 write() 与 read() 读写文件,通过 sendmsg() 和 recvmsg() 系统调用读写网络,这些系统调用是内核子系统封装的统一且稳定的API。而操作系统也提供对于底层驱动和内核的配置接口,如 sysfs,netlink,procfs等接口,这些接口可以改变系统运行行为,添加删除硬件设备等。
- 在这些系统调用之下是虚拟文件系统(VFS),TCP/IP 网络栈等内核子系统。这些子系统会完成用户态发起的系统调用,并返回相应的结果给用户空间程序。
- 内核态直接与硬件交互,管理硬件资源,并提为内核子系统提供原始数据及硬件抽象。
而 BetterProfiler 的位置在系统调用进入内核的最开始,以及分布在每个内核子系统发起系统调用的期间:
所以 BetterProfiler 能获取到的信息非常多且细致,他能捕获应用加密层生成的随机数,捕获事件发生时的准确的Unix时间戳,操纵网络数据包和转发逻辑,获取进程 cgroup 上下文等,理论上是无所不能的。
抵御恶意操纵
每年护网期间,一个从始至终的话题就是“火墙极其相关安全设备是怎么被黑掉的”。实际上,安全设备在很多时候反而容易成为内网的安全弱点,因为安全设备本来就处于敏感地带的出口区域,且所有数据都会经过安全设备审计,如果安全设备内的软件栈被恶意操纵,那么参与护网的厂商就离出局不远了。所以如何抵御外部的恶意操纵是一个非常重要的话题。
BetterProfiler 有完善的抵御外部恶意操作的逻辑,在程序成功完成验证进入内核之后,eBPF 程序会根据程序是从特权进程还是非特权进程加载来运行一个加固过程。此步骤包括:
- 程序执行保护:保存 eBPF 程序的内核内存受到保护并设为只读。无论是内核错误还是恶意操纵,试图修改 eBPF 程序内核立即崩溃,阻断 Rootkit 通过 BetterProfiler 的代码执破坏系统。
- 常量盲化:代码中的所有常量都被盲化,以防止 JIT 喷射攻击。这可以防止攻击者将可执行代码作为常量注入,在存在另一个内核错误的情况下,攻击者可能会跳转到 BetterProfiler 程序的内存部分来执行代码。
作品测试与分析
本次测试环境如下:
内核:Linux 5.10.12+
底层C库:musl libc
文件系统:busybox
内存:64 MB
FLASH存储:512 MB
监控某敏感文件是否被读取
假设我们的敏感文件在下 /tmp/login.txt
,监控系统进程中对 /tmp/login.txt
的读写,我们模拟的操作如下:
id 为 1000 的用户使用 cat 命令查看了 /tmp/login.txt 文件,返回 empty
id 为 1000 的用户使用使用 echo 命令写入 passwd 12345 到 /tmp/login.txt 文件
id 为 1000 的用户使用使用 vim 编辑了 /tmp/login.txt 文件
id 为 1000 的用户使用最终删除了 /tmp/login.txt 文件
使用如下命令发起对 /tmp/login.txt
的监控:
sudo /tmp/bfer/dist/bfer --trace event=openat --trace openat.pathname="/tmp/login*"
BetterProfiler 监控到的内容如下:
BetterProfiler 很好的记录了id为 1000 的用户对 /tmp/login.txt
发起的每一次操作,包括时间戳,运行的命令,PID号,对/tmp/login.txt
操作所发起的系统 openat()
调用,以及openat()
系统调用参数:
dirfd: -100, pathname: /tmp/login.txt, flags: , mode: 0
监视某张网卡上的链接,并反推恶意链接发起者
我们模拟的行为如下:
恶意 ip 为 192.168.31.1,域名为 zzh.fuck
id 为 1000 的用户使用 后门程序 来对 zzh.fuck 的 80 端口 发起请求 tcp 请求
id 为 1000 的用户使用 后门程序 来对 zzh.fuck 的 8080 端口 发起请求 tcp 请求
使用一下命令监控这种网卡的所有数据:
sudo /tmp/bfer/dist/bfer --trace set=network_events --trace net=wlan0
BetterProfiler 监控到的内容如下:
可以看到 BetterProfiler 同样记录了每次恶意请求的时间戳,发起请求的内核事件为 net_packet 组,且推导出了恶意程序的PID与TID号,同时记录了出站和入站的 IP 端口信息,以及出站入站所占用的物理网卡名称,以及每次请求的数据包荷载大小。
监视某恶意程序的所有行为,并导出为 JSON 文件审查
我们模拟的行为如下,编写一个恶意程序,使用 exec 运行其恶意代码,记录其所有系统调用行为,并审计敏感行为。
假设我们的恶意程序伪装成为 curl ,其恶意行为如下:
向 zzh.fuck:8080 端口发送恶意 payload
使用如下命令监控此二进制程序:
sudo /tmp/bfer/dist/bfer --output json --trace comm=curl | jq
输出了大量的日志信息,BetterProfiler 记录了这个程序的所有系统调用,以及调用参数,参数内的地址,我们可以根据这些信息推测该恶意程序的恶意行为,甚至是无视其加密层提取其传输内容。
以下是日志的一部分,恶意程序使用了security_file_open 定位到了 /tmp/login.txt
"eventName": "security_file_open",
"argsNum": 6,
"returnValue": 0,
"stackAddresses": null,
"args":
{
"name": "pathname",
"type": "const char*",
"value": "/tmp/login.txt"
},
{
继续跟踪其行为:
"eventName": "security_socket_create",
"argsNum": 4,
"returnValue": 0,
"stackAddresses": null,
"args": [
{
"name": "family",
"type": "int",
"value": 2
},
{
"name": "type",
"type": "int",
"value": 2
},
该程序使用系统调用 security_socket_create 创建了一个 socket 管道,
其参数为:
{
"name": "remote_addr",
"type": "struct sockaddr*",
"value": {
"sa_family": "AF_INET",
"sin_addr": "192.168.31.68",
"sin_port": "8080"
}
}
而在模拟环境中,恶意域名对应的ip为192.168.31.68,端口为 8080,可以初步判断其为窃取用户口令的恶意软件。
性能测试
监控系统所有发起网络连接的程序极其内部调用链条,BetterProfiler 占用CPU约 30%,占用内存50M,30秒输出日志 920M,涵盖系统当前所有对外发起的链接,以及对应的程序,时间戳,所属用户,发起的系统调用名称,文件描述符,函数参数,函数返回地址等。
对于某个程序进行24小时持续跟踪,占用CPU约 5%,内存占用3MB,涵盖该程序当前所有对外发起的链接,以及对应的程序,时间戳,所属用户,发起的系统调用名称,文件描述符,函数参数,函数返回地址等。
监听全局网络读写时,万兆吞吐量下 TCP 读写速度下降 3-5%,相较于 Iptables 万兆吞吐量下 TCP 收发基本处于不可用状态
监听全局文件读写时,磁盘 IO 能效基本没有下降。
总结
BetterProfiler 可以像 Wireshark 一样抓取整个系统的事件响应,这些时间是一个不断增长的巨大列表,几乎涵盖Linux内核的各个角落,可帮助开发人员:
- 调试与理解系统行为
- 对恶意软软件进行逆向分析
- 录制软件整个生命周期
- 学习Linux 内核原理
- 进行漏洞挖掘
BetterProfiler 非常轻量,对系统运行负载极低,并且解决了 eBPF 的可移植性问题,对运行环境基本没有需求,其不依赖其他组件。
BetterProfiler 运行在 eBPF 虚拟机中,虽然运行在内核态中,但其行为却较为克制,以一种非入侵的方式接管安全人员感兴趣的内核安全路径,软件自身也考虑到了极端情况下的安全性,例如一个精心设计的恶意程序触发了 BetterProfiler自身代码缺陷导致安全漏洞,在这种情况下 eBPF将会立刻引发内核崩溃,阻止危害扩大。
BetterProfiler 提供标准化日志接口(json和xml),可以与现有安全产品进行对接,如防火墙,日志审计系统等,开发者还可编写自己的前端 BetterProfiler 进一步可视化系统内部的多种行为。可作为端点防御和病毒动态监测引擎。
创新性说明
实话没有多少创新的成分,单纯觉得当下Linux上的 动态监测和监控没有好用的方案,Linux上的额跟踪技术五花八门,各自为营,切需要很强的调试能力。Linux上的安全模块没有统一的解决方案,防火墙使用iptables,沙箱使用 secomp,跟踪使用 strace或者ftrace,切相同的功能重复率高。所以有人说 Linux 不需要安全软件,因为里头全是安全软件。
但随着国产化的步伐,很多国企内网都放弃了Windows转向自主研发的基于 Linux 内核的操作系统。所以 Linux 上势必需要一个无需配置,使用简单,目的明确的端点安全软件,而这一块基本上还是空白。
BetterProfiler 借鉴了 bpftrace & bcc 和设计,目的是使用 eBPF 重新造一个可用且好用端点安全动态分析引擎,让安全软件栈从单一的流量监测和静态分析跳脱出来的一次大胆尝试,让嵌入式 Linux 系统,如摄像头,路由器,GPON设备,物联网关也具有单机安全防御能力。
写道这里我都不知道我究竟写了什么,或者我究竟在尝试表达什么,这很牛皮吗,或者这很新颖吗,搞得好像 BPF 框架是是我写的一样。但是比赛的文档就需要这样写啊,不是吗?
不会再有二次了。