前言
在《聊一聊《Bvp47 美国NSA方程式的顶级后门》中的BPF隐藏信道》一文里,我们讨论了二十年前基于BPF技术后门的功能实现,技术原理等。也展望也在二十年后的今天,eBPF技术加持下,有更多可以被后门利用的技术点,可以造成更大的破坏力。
同时,在《Linux中基于eBPF的恶意利用与检测机制》介绍了eBPF的恶意利用,与防御相关的检测防御机制等。笔者对当下国内IDC对eBPF防御建设的不完善感到忧心,综合国外DefCon等安全峰会中的话题内容,预判未来一段时间,基于eBPF的后门会越来越多,越来越隐秘。
就在一个多月前,笔者看到了一款基于eBPF技术后门Boopkit,上传到https://github.com/kris-nova/boopkit,今天,就跟大家一起来了解一下它的技术原理。
boopkit介绍
boopkit是基于eBPF技术实现的Linux后门rootkit,利用任意开启的TCP端口服务,远程唤醒后门,生成反弹shell,具备远程命令执行等能力。
此项目是在2022年3月30日首次创建,至今1个多月,已经有近1000星,在Twitter上也有一定热度,持续传播中。用户空间的加载器与内核态代码都是纯C编写。部分脚本使用Makefile、shell等脚本语言。
作者 Kris Nóva 是一位女性安全工程师,经常在twitch上直播写代码,有兴趣的同学可以去围观。
功能
GitHub上Boopkit介绍:
Remote code execution over TCP (SSH, Nginx, Kubernetes, etc) Network gateway bypass (bad checksums, TCP reset) Self obfuscation at runtime (eBPF process hiding)
很容易看出,这是后门工具,利用当前服务器开启的任意TCP端口作为通讯隧道,比如SSH、Nginx、Kubernetes等常用服务。利用eBPF技术,在HOOK tracepoint的tcp_bad_csum
、tcp_receive_reset
这两个TCP内核函数,作为敲门、传递信息的机制,进行通讯。并具备进程的自我隐藏等能力。
相比之前《聊一聊BVP47》的SYN窍门,有异曲同工之妙。但多了传送RCE shell等更多利用,当然,也是在高版本eBPF实现的功能。
安装测试
环境依赖
编译依赖项目说明中,依赖如下:
clang bpftool Required for libbpf xdp-tools Required for libxdp llvm pcap lib32-glibc
笔者环境是ubuntu 22.04的Desktop,内核版本是 5.15的。理论上其他低点的版本也可以运行。
笔者的clang
、llvm
都是12,bpftool
在项目编译时不需要,但需要安装libbpf-dev这个开发包,依赖头文件。在ubuntu 22.04上,libbpf-dev
是0.5.0
的版本,版本太低,可以下载libbpf-0.6.1版本,安装使用。
xdp-tools
、pcap
也都是需要开发的头文件,记得安装devel版本的类库。
lib32-glibc
包是ArchLinux发行版上的包名字,在其他Linux发行版上一般叫glibc
,对应开发库libc6-dev-i386
等。
内核依赖内核版本上项目没说明,理论5.x都行,4.x请自测。但配置上,需要启用tcp_bad_csum
、tcp_receive_reset
两个函数的tracepoint。可以检查/sys/kernel/debug/tracing/events/tcp
目录下是否有这两个目录。
运行依赖客户端在启动后,需要监听端口,等待后门的反弹shell链接过来。使用的是ncat程序,需要提前安装好。ubuntu上命令sudo apt install ncat
。
命令参数
Boopkit后门分为两部分:服务端:程序名boopkit,运行在被入侵的服务器上。客户端:程序名boopkit-boop,黑客使用的控制端。
服务端
程序名是boopkit
,编译好后,会把ebpf的字节码文件写入到/root/.boopkit/
目录下,有三个文件:
-
pr0be.boop.o -
pr0be.safe.o -
pr0be.xdp.o
pr0be.boop.o
用于TCP的网络HOOK,实现敲门以及网络RCE接收。pr0be.safe.o
用于后门进程的自我保护,启动后,在进程列表中隐藏自己。pr0be.xdp.o
截止至2022-05-06,还是空的,没有逻辑。从作者直播中提到,应该是打算用于替换掉libpcap
方式的数据包拦截、解析,以及提取RCE,用于更好地隐藏自己。
该程序命令帮助如下:
Boopkit. Linux rootkit and backdoor. Built using eBPF.
Usage: boopkit [options]
Options: -h, help Display help and usage for boopkit. -i, interface Interface name. lo, eth0, wlan0, etc -s, sudo-bypass Bypass sudo check. Breaks PID obfuscation. -r, reverse-conn Attempt a reverse RCE lookup if no payload found. -q, quiet Disable output. -x, reject Source addresses to reject triggers from.
参数说明
interface-i
参数将用于libpcap
的网络嗅探的目标网卡,用于提取RCE。若走eBPF XDP方式,则不需要指定网卡,都可以拿到,那么后门的适应性更强。只是作者还没实现。
sudo-bypass-s
参数用于躲避sudo用户检测。
reverse-conn-r
参数是配置参数,当前后门是否启用反弹shell模式。反弹shell模式交互好,但隐蔽性不好。该后门也提供了秘密执行的功能,下文会提到。
quiet-q
参数是静默模式,不输出日志。
reject-x
参数算是后门的扩展功能,用于黑名单IP的筛选,意义不大。
前面介绍了后门的参数功能,下面讲下后门对RCE执行的两种方式,以及对应代码实现。
直接执行
使用libpcap库读取网络通讯包,筛选出黑客的IP,读取TCP的payload信息,匹配特征字符串BOOPKIT_RCE_DELIMITER
,读取RCE信息。再调用系统system
函数执行。
#define BOOPKIT_RCE_DELIMITER "X*x.x*X"
#define BOOPKIT_RCE_CMD_HALT "X*x.HALT.x*X"
//boopkit.c
xcap_found = xcaprce(saddrval, rce);
if (xcap_found == 1) {
exec(rce);
bpf_map_delete_elem(fd, &jkey);
ikey = jkey;
continue;
}
// dpi.c
// Search
for (int i = 0; i < XCAP_BUFFER_SIZE; i++) {
struct xcap_ip_packet *xpack;
xpack = snap[i];
if (!xpack->captured) {
continue;
}
char *xpack_saddr = inet_ntoa(xpack->iph->ip_src);
char *ret = strstr(search, xpack_saddr);
if (!ret) {
continue; // Filter packets not from our IP address
}
// Begin DPI
unsigned char *packet = xpack->packet;
char *rce_sub;
rce_sub = memmem(packet, xpack->header->caplen, BOOPKIT_RCE_DELIMITER,
strlen(BOOPKIT_RCE_DELIMITER));
if (rce_sub != NULL) {
boopprintf(" -> Found RCE xCap!n");
int found;
found = rce_filter(rce_sub, rce);
// Flush the snapshot
xcap_ring_buffer_free(snap);
// Flush the main ring buffer
xcap_ring_buffer_free(xcap_ring_buffer);
xcap_ring_buffer_init(xcap_ring_buffer);
if (found) {
return 1;
} else {
boopprintf(" XX [FILTER FAILURE] No RCE in xCap!n");
return 0;
}
}
}
也可以通过RCE中包含特定字符串,来关停后门。
反弹shell
若后门启用反弹shell模式,则可以让这台boopkit的机器主动向黑客监听端口发起TCP链接。隐蔽性不如上面一种模式,但交互比较好,可以只需不停的读取黑客命令,并调用系统system
函数执行。
retval = recvrce(saddrval, rce);
if (retval == 0) {
exec(rce);
bpf_map_delete_elem(fd, &jkey);
ikey = jkey;
continue;
}
eBPF HOOK
后门一共hook了两类eBPF功能点,TCP与进程列表等几个运行时函数。
eBPF TCP HOOK
pr0be.boop.c内容比较简单,实现了tcp_bad_csum
与tcp_receive_reset
两个函数HOOK,功能逻辑也比较简单,就是读取TCP包的来源IP,并写入eBPF map,用于存放黑客认证IP。IP将在libpcap网络包读取部分使用。
SEC("tracepoint/tcp/tcp_bad_csum")
SEC("tracepoint/tcp/tcp_receive_reset")
pr0be.safe.c代码稍微多了一点点,业务功能是实现了当前后门进程的PID隐藏,HOOK了多个函数点:
SEC("tp/syscalls/sys_enter_getdents64")
SEC("tp/syscalls/sys_exit_getdents64")
SEC("tp/syscalls/sys_exit_getdents64")
HOOK点之间,使用多张eBPF Map存储数据,一部分用于内核空间跟用户空间通讯,另外一部分用于多个HOOK点之间通讯。使用bpf_tail_call
尾调用实现隐藏逻辑控制。
pr0be.xdp.c
该文件目前是空,前面也提到了,这个应该是作者用于替换libpcap
实现网络数据读取的功能,等待实现。
客户端
程序名是boopkit-boop
,功能是构造畸形或带RCE payload的TCP数据包,发送到服务端,触发服务端内核的tcp_bad_csum
、tcp_receive_reset
函数,对Boopkit后门敲门。
后门程序封装了一个SHELL脚本,启动控制boopkit-boop
客户端端更方便。默认启用反弹shell模式,并使用ncat
程序监听3545
端口,等待被入侵服务器主动连接过来。
boopkit运行原理
Boop敲门Vectors
Boopkit(服务端)会响应各种网络事件,boopkit-boop(客户端)工具内置了两种敲门机制。
其利用的TCP帧原理如下图:
无效的Checksum
boopkit-boop客户端
工具将通过SOCK_RAW Socket 向boopkit服务器发送一个带有空校验和的畸形 TCP SYN 数据包。不管这台服务器正在运行什么TCP服务,都会成功敲门,触发eBPF的TCP HOOK。在Linux服务器通吃。
⚠️注意个别硬件网卡将丢弃所有格式错误的校验和数据包,该工具失效!
发送ACK-RST包
boopkit-boop客户端
工具将使用 SOCK_STREAM Socket针对目标的TCP服务(如SSH、Kubernetes、Nginx等)先正常完成TCP握手后,会关闭该TCP链接,以确保这个服务是正常启动的。
之后,再重复该过程,将重置数据包中的TCP RESET标志位,触发服务器上eBPF TCP HOOK。
视频演示
检测机制
行为
网络行为
网络抓包可以抓到敲门的包,但如果用XDP实现,是可以做到读取后,直接XDP_DROP
掉,抓包就抓不到了。隐秘性更可怕。
从图中,可以看到,Checksum
的值是0,且后面的TCP Segment已经被改成不合法的,无法解析的包。RCE也被放置在payload中。(两头加了BOOPKIT_RCE_DELIMITER标记)
端口监听
后门不监听端口,隐藏进程,普通工具查不出来。
进程
RCE执行时,会有记录,但进程的父ID查不出来。top
命令却能直接看到,也能直接kill掉,隐藏的不够好。
基于eBPF的特征
事中
GitHub上,笔者开源了一个基于eBPF实现的HIDS入侵检测系统ehids-agent DEMO,具备基本的BPF Call调用检测,可以来测试一下能否发现。
为了减少干扰信息,笔者这里只开启了eHIDS的bpf call monitor
模块。
cfc4n@vm-desktop:~/project/ehids-agent$ sudo ./bin/ehids-agent
[sudo] password for cfc4n:
2022/05/07 00:21:53 https://github.com/ehids/ehids-agent
2022/05/07 00:21:53 process pid: 69409
2022/05/07 00:21:53 start to run EBPFProbeBPFCall module
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:1, Cmd:BPF_PROG_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
2022/05/07 00:21:59 probeName:EBPFProbeBPFCall, probeTpye:tracepoint, BPFCALL EVENT CPU:0, Cmd:BPF_BTF_LOAD, PID:69416, UID:0, Comm:boopkit, cmdline:./boopkit -i ens33, utsName:vm-desktop
可以看到,能精准地监控到调用的BPF指令,进程ID,进程名,命令行参数等。
事后
该后门,在进程PID隐藏上做了实现,但在bpf的行为特征上,目前还没有实现。可以用bpftool
等常规工具来检查它。
prog
root@vm-desktop:~# bpftool prog show
216: tracepoint name handle_getdents tag d5fabe9da09eec5e gpl
loaded_at 2022-05-07T00:25:16+0800 uid 0
xlated 144B jited 93B memlock 4096B map_ids 135,129
btf_id 180
218: tracepoint name handle_getdents tag d197d702eb020291 gpl
loaded_at 2022-05-07T00:25:16+0800 uid 0
xlated 1216B jited 841B memlock 4096B map_ids 129,130,135,132,131
btf_id 180
219: tracepoint name handle_getdents tag e938383ee7761b31 gpl
loaded_at 2022-05-07T00:25:16+0800 uid 0
xlated 544B jited 310B memlock 4096B map_ids 132,133,135
btf_id 180
222: tracepoint name tcp_bad_csum tag 82cc666e25685b71 gpl
loaded_at 2022-05-07T00:25:16+0800 uid 0
xlated 744B jited 404B memlock 4096B map_ids 138
btf_id 181
223: tracepoint name tcp_receive_res tag c25930e6076718fc gpl
loaded_at 2022-05-07T00:25:16+0800 uid 0
xlated 416B jited 227B memlock 4096B map_ids 138
btf_id 181
map
如上,一样的方法。
root@vm-desktop:~# bpftool map show
129: hash name map_buffs flags 0x0
key 8B value 8B max_entries 8192 memlock 131072B
btf_id 180
130: hash name map_bytes_read flags 0x0
key 8B value 4B max_entries 8192 memlock 131072B
btf_id 180
131: prog_array name map_prog_array flags 0x0
key 4B value 4B max_entries 5 memlock 4096B
owner_prog_type tracepoint owner jited
btf_id 180
132: hash name map_to_patch flags 0x0
key 8B value 8B max_entries 8192 memlock 131072B
btf_id 180
133: ringbuf name rb flags 0x0
key 0B value 0B max_entries 8192 memlock 0B
135: array name pr0be_sa.rodata flags 0x480
key 4B value 24B max_entries 1 memlock 4096B
btf_id 180 frozen
136: array name pr0be_sa.bss flags 0x400
key 4B value 20B max_entries 1 memlock 4096B
btf_id 180
138: hash name event flags 0x0
key 4B value 32B max_entries 8192 memlock 327680B
btf_id 181
139: array name pr0be.bss flags 0x400
key 4B value 20B max_entries 1 memlock 4096B
btf_id 181
检测防御
可参考 《Linux中基于eBPF的恶意利用与检测机制》一文。
结束语
当前后门发往外部的TCP通讯是可监控,对网络捕获的实现是libpcap库,特征比较明显,而这个程序作者不停改进中。业界中,有人分享过更隐秘的技术方式,比如在内核态,复用业务的网络包,更改内容发数据包给黑客。躲避了用户态的检查。
这后门虽然不完美,但这只是GitHub开源社区的现状,相信在黑市里,早有隐秘性笔者高很多的后门在使用。笔者再次呼吁,务必加快对eBPF恶意利用的防御检测体系建设。
参考资料
[1]聊一聊《Bvp47 美国NSA方程式的顶级后门》中的BPF隐藏信道
[2]
[3]
项目boopkit: https://github.com/kris-nova/boopkit
[4]
作者Kris Nóva: https://twitter.com/krisnova
[5]
kris Nova twitch: https://www.twitch.tv/krisnova
[6]
libbpf-0.6.1: https://github.com/libbpf/libbpf
[7]
ehids-agent: https://github.com/ehids/ehids-agent
原文始发于微信公众号(榫卯江湖):新型eBPF后门boopkit的原理分析与演示