新型eBPF后门boopkit的原理分析与演示

前言

在《聊一聊《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,具备远程命令执行等能力。

kris-nova/boopkit

此项目是在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_csumtcp_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的。理论上其他低点的版本有可以运行。

笔者的clangllvm都是12,bpftool在项目编译时不需要,但需要安装libbpf-dev这个开发包,依赖头文件。在ubuntu 22.04上,libbpf-dev0.5.0的版本,版本太低,可以下载libbpf-0.6.1版本,安装使用。

xdp-toolspcap也都是需要开发的头文件,记得安装devel版本的类库。

lib32-glibc包是ArchLinux发行版上的包名字,在其他Linux发行版上一般叫glibc,对应开发库libc6-dev-i386等。

内核依赖
内核版本上项目没说明,理论5.x都行,4.x请自测。但配置上,需要启用tcp_bad_csumtcp_receive_reset两个函数的tracepoint。可以检查/sys/kernel/debug/tracing/events/tcp目录下是否有这两个目录。

tracepoint events

运行依赖
客户端在启动后,需要监听端口,等待后门的反弹shell链接过来。使用的是ncat程序,需要提前安装好。ubuntu上命令sudo apt install ncat

命令参数

Boopkit后门分为两部分:
服务端:程序名boopkit,运行在被入侵的服务器上。
客户端:程序名boopkit-boop,黑客使用的控制端。

服务端

程序名是boopkit,编译好后,会把ebpf的字节码文件写入到/root/.boopkit/目录下,有三个文件:

  1. pr0be.boop.o
  2. pr0be.safe.o
  3. 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_csumtcp_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_csumtcp_receive_reset函数,对Boopkit后门敲门。

后门程序封装了一个SHELL脚本,启动控制boopkit-boop客户端端更方便。默认启用反弹shell模式,并使用ncat程序监听3545端口,等待被入侵服务器主动连接过来。

boopkit运行原理

how boopkit works

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恶意利用的防御检测体系建设。

微信公众号

回复boopkit获取该文章链接。

知识共享许可协议CFC4N的博客CFC4N 创作,采用 署名—非商业性使用—相同方式共享 4.0 进行许可。基于https://www.cnxct.com上的作品创作。转载请注明转自:新型eBPF后门boopkit的原理分析与演示

4 thoughts on “新型eBPF后门boopkit的原理分析与演示

  1. 您好,首先感谢您的分享和细致的分析。我是安全技术爱好者,看了您的文章很感兴趣。但是复现boopkit时总是出现问题,想向您请教如何解决如下问题:
    -> getuid() : 0
    -> getpid() : 26808
    -> getppid() : 21301
    -> Logs : /sys/kernel/tracing/trace_pipe
    -> Loading eBPF Probe : /root/.boopkit/pr0be.safe.o
    -> Starting xCap Interface : lo
    -> Initalizing Ring Buffer
    -> eBPF Probe Loaded : /root/.boopkit/pr0be.safe.o
    -> Loading eBPF Probe : /root/.boopkit/pr0be.boop.o
    -> eBPF Probe Loaded : /root/.boopkit/pr0be.boop.o
    -> eBPF Program Attached : tp/tcp/tcp_bad_csum
    -> eBPF Program Attached : tp/tcp/tcp_receive_reset
    Segmentation fault (core dumped)

Comments are closed.