LKM 型 Rootkit,一般编写内核模块加载到内核中,挂钩系统调用、虚拟文件系统等,改变内核函数执行流程,实现一系列 Rootkit 功能,比如:
保持对受感染机器的访问
实现各类隐藏功能
创建 root 权限后门
劫持或关闭安全程序
任何小错误都可能导致 kernel panic
内核的任何更新都可能引发新的运行错误
这些影响增加了系统的不稳定性,直接暴露攻击行为。希望存在一种更加通用方便快捷的攻击方式,来实现 Rootkit 功能。
具备“内核超能力”的 eBPF 受到安全人员的关注。
1. eBPF超能力
eBPF 全称扩展伯克利包过滤,最初设计用于快速数据包处理,对系统内核进行观测的技术,一般用于性能、网络、安全的追踪和处理。
eBPF 程序是事件驱动的,并在内核或者应用程序通过某个跟踪点时运行。预定义的跟踪点包括系统调用、函数入口/退出、内核函数跟踪点、Socket、TC、XDP 层的网络事件等。
如果不存在满足特定需求的预定义跟踪点,可以创建内核探针(kprobe)或用户探针(uprobe)在内核或用户应用程序的几乎任何位置挂钩 eBPF。
eBPF 程序的首先使用 C 或 Rust 编写 eBPF 程序,LLVM 编译为字节码,用户态程序,通过一些 eBPF 库,使用 bpf 系统调用将 eBPF 字节码加载到 Linux 内核。
内核 eBPF 验证器验证:
发起 bpf 系统调用的进程是否具有相应权限,要求进程具有相关的 Linux Capabilities(CAP_BPF)或 root 权限;
检查程序是否会导致内核崩溃,例如是否有未初始化的变量,是否有可能导致数组越界、空指针访问的语句;
eBPF 程序在完成构建后,挂载到内核的对应事件,如,当某个系统调用产生时,触发内核调用对应的 eBPF 程序。内核 eBPF 程序通过 map 数据结构与用户态程序进行交互,完成相应功能。
Linux 内核模块与 eBPF 之间的对比:
$ sudo apt install build-essential git make libelf-dev clang llvm strace tar bpfcc-tools linux-headers-$(uname -r) gcc-multilib flex bison libssl-dev -y
$ apt-cache search linux-source
linux-source - Linux kernel source with Ubuntu patches
linux-source-5.15.0 - Linux kernel source for version 5.15.0 with Ubuntu patches
$ apt install linux-source-5.15.0
$ ls -hl
drwxr-xr-x 4 root root 4.0K 5月 19 10:24 linux-source-5.15.0
lrwxrwxrwx 1 root root 47 5月 5 17:45 linux-source-5.15.0.tar.bz2 -> linux-source-5.15.0/linux-source-5.15.0.tar.bz2
$ tar -jxvf linux-source-5.15.0.tar.bz2
$ cd linux-source-5.15.0
$ make scripts # 可选
$ cp -v /boot/config-$(uname -r) .config # make defconfig 或者 make menuconfig
$ make headers_install
$ make M=samples/bpf # 如果配置出错,可以使用 make oldconfig && make prepare 修复
如果无法从 vmlinux 生成 vmlinux.h,则 samples/bpf 构建失败。
samples/bpf/Makefile:369: *** Cannot find a vmlinux for VMLINUX_BTF at any of " /usr/src/linux-source-5.15.0/vmlinux", build the kernel or set VMLINUX_BTF variable。停止。
make: *** [Makefile:1875:samples/bpf] 错误 2
[[email protected] boot]# od -t x1 -A d vmlinuz | grep "1f 8b 08"
0013408 ff e0 1f 8b 08 00 ea 80 b9 52 02 03 ec 5b 7f 74
[[email protected] boot]# dd if=vmlinuz bs=1 skip=0013410 | zcat > vmlinux
gzip: stdin: decompression OK, trailing garbage ignored
记录了9195934+0 的读入
记录了9195934+0 的写出
9195934字节(9.2 MB)已复制,51.5023 秒,179 kB/秒
0013408 ff e0 1f 8b 08 00 ea 80 b9 52 02 03 ec 5b 7f 74
dd if=./vmlinuz skip=`grep -a -b -o -m 1 -e $'\x1f\x8b\x08\x00' ./vmlinuz | cut -d: -f 1` bs=1 | zcat > /tmp/vmlinux
chmod +x extract-vmlinux
./extract-vmlinux vmlinuz > vmlinux
$ strings vmlinux | grep sbin
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PATH=/sbin:/bin:/usr/sbin:/usr/bin
/sbin/bridge-stp
/sbin/modprobe
/sbin/poweroff
/sbin/hotplug
hook 系统调用和用户空间函数调用
操作用户空间数据结构
修改系统调用返回值
调用 system() 创建新进程(bpftrace内置特性)
某些 BPF 程序可以直接操作硬件设备(如网卡)
针对 BPF 代码的供应链攻击的可能性
配合安全检测工具使用(例如,hook bpf自身)
eBPF 几乎可以实现内核态 Rootkit 所有的隐藏功能,一般的内核模块的挂钩系统调用、虚拟文件系统,eBPF 程序可以使用跟踪点程序类型完成相同功能。针对网络行为,eBPF 程序也可以使用 XDP/TC 等类型完成对数据包的控制,而且流量处理能力更强。
eBPF 利用可以分为两个方面,网络侧和系统侧,分别通过网络类型程序和跟踪点类型程序实现,端口敲门、链路控制、权限提升、隐藏恶意程序行为等。
下面先从网络侧介绍具体的攻防场景。
在网络侧,eBPF 程序挂载到不同层次,可以实现控制链路、敲门和读取数据包内容。
● 控制链路
防火墙前读写包,篡改源和目标 IP 和端口
劫持现有连接
● 端口敲门
● 劫持数据包
eBPF 程序挂载到协议层,处理来自特定协议的数据包实现敲门;
挂载到 TC 层,可以控制出口和入口流量,修改数据包;
挂载到 XDP 层,由于在 XDP 处理时,还没有将数据包复制到内核 sk_buffer,可以先于内核其他层处理数据包,尤其是可以在防火墙前读写数据包,篡改数据。但是 XDP 只能控制入口流量。
下图定义了一个 XDP 类型 BPF 程序,此程序对入口流量进行解析判断,通过以太头获取 IP 头, IP 头获取 TCP 头,对目的端口进行判断,如果等于 6666端口,则对以太帧里的 MAC 地址进行替换,如果不是则通过。当做完特定操作后,返回 XDP_DROP 可以丢弃数据包,此时使用 tcpdump 等抓包工具无法获取到此数据包。
BPFtrace 是一个 BPF 开发的前端工具,可用来创建自定义的 BPF 程序,而不需要处理太多底层技术细节。在 bpftrace 主页中,将之称为 “Linux 系统中的高级跟踪语言 “。
BPFtrace 有一个方便的内置函数可以执行任意系统命令:
//--unsafe选项允许执行不安全的内置函数。
bpftrace --unsafe -e 'BEGIN { printf("Hello Offensive BPF!\n"); system("whoami"); }'
攻击利用流程:
攻击者获得了某台主机的特权访问
攻击者安装一个基于 BPFtrace 的后门程序
只要消息来自某个 IP(或源端口),恶意程序就会运行
backdoor
加载backdoor后,通过bpftrace挂钩,kretprobe:inet_csk_accep
收到来自攻击者特定IP的特定端口发起的流量
视频演示 backdoor 攻击流程,rkdetect 对加载到系统的 kprobe 检测,以及使用 bpflist-bpfcc 和 bpftool 取证的整个过程。
服务端:boopkit,运行在被控端
攻击利用流程:
攻击者获得了某台主机的特权访问
攻击者安装一个基于 eBPF 的 TCP 后门
只要消息来自某个 IP(或源端口),恶意程序就会运行
boopkit 所需依赖:
clang
bpftool Required for libbpf
xdp-tools Required for libxdp
llvm
pcap
lib32-glibc
boopkit 参数选项:
interface -i 参数指定 libpcap 监听的目标网卡。若使用 eBPF XDP 方式,则不需要指定网卡
sudo-bypass -s 参数规避sudo用户检测
reverse-conn -r 参数是配置参数,是否启用反弹 shell 模式
quiet -q 参数是静默模式,不输出日志
reject -x 参数设置黑名单 IP
两种敲门方式
第一种,发送无效的校验和,boopkit-boop 将通过 SOCK_RAW Socket 向 boopkit 被控端发送一个带有空校验和(Checksum)的 TCP SYN 数据包。(boopkit-boop.c)
第二种, 发送ACK RST包,利用 SOCK_STREAM Socket 针对目标的 TCP 服务(如SSH、Nginx、MySQL等),先正常完成TCP握手后,然后会关闭该 TCP 连接,将重置数据包中的TCP RESET标志位,再重复该过程,发送ACK RST 包进行敲门。(boopkit-boop.c)
敲门成功后服务端主动向客户端 3545 端口,发起连接,客户端返回反弹shell 相关的配置信息,服务端读取后执行反弹 shell 命令,建立新的shell连接。而服务端也挂钩了 getdents 相关的系统调用,将 eBPF 程序自身进程隐藏。(pr0be.safe.c)
SEC("tp/syscalls/sys_enter_getdents64")
SEC("tp/syscalls/sys_exit_getdents64")
SEC("tp/syscalls/sys_exit_getdents64")
https://www.ebpf.top/post/ebpf_c_env/
https://www.ebpf.top/post/offensive-bpf-bpftrace/
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=ec24704492d8
https://www.cnblogs.com/charlieroro/p/14244276.html
https://www.cnxct.com/ebpf-rootkit-how-boopkit-works/
https://embracethered.com/blog/posts/2021/offensive-bpf-bpftrace/
https://www.brendangregg.com/BPF/bpftrace-cheat-sheet.html
https://github.com/xdp-project/xdp-tools/releases/tag/v1.2.3
https://www.libhunt.com/r/bad-bpf
https://blog.tofile.dev/2021/08/01/bad-bpf.html
https://www.blackhat.com/us-21/briefings/schedule/#with-friends-like-ebpf-who-needs-enemies-23619