EBPF恶意利用及防御

百家 作者:平安安全应急响应中心 2022-05-12 22:13:35

本篇记录一下利用EBPF技术实现HIDS系统agent端部分功能的研究,及部分例子的实验情况。

0x01 研究细节


此处就不再对BPF技术及其使用做详细介绍了。

这里粗略提一下使用EBPF技术做恶意利用时候的优点:
  • 稳定,EBPF程序出错时不会影响内核运行。

  • 更不易被感知,即使是内核模块方式实现的HIDS。


一、恶意利用


1. 网络层


下面以一个简单的XDP程序为例。例子实现将目的端口为9999的UDP包转发给端口9998。

a. 依赖库BCC

BCC库安装参考:https://github.com/iovisor/bcc/blob/master/INSTALL.md


b. 代码文件:


  • 用户层XDP Loader
main.py
#!/usr/bin/env python3
from bcc import BPFimport time

device = "lo"b = BPF(src_file = "filter.c")fn = b.load_func("udpfilter", BPF.XDP)b.attach_xdp(device, fn, 0)
try: b.trace_print()except KeyboardInterrupt: pass
b.remove_xdp(device, 0)
基于BCC框架的XDP程序的加载器。

  • XDP程序
filter.c
#include <linux/bpf.h>#include <linux/if_ether.h>#include <linux/ip.h>#include <linux/in.h>#include <linux/udp.h>

#define KBUILD_MODNAME "filter"
int udpfilter(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data; void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data; if((void*)eth + sizeof(*eth) <= data_end) { struct iphdr *ip = data + sizeof(*eth); if((void*)ip + sizeof(*ip) <= data_end) { if(ip->protocol == IPPROTO_UDP) { struct udphdr *udp = (void*)ip + sizeof(*ip); if ((void*)udp + sizeof(*udp) <= data_end) { if (udp->dest == ntohs(9999)) { bpf_trace_printk("UDP Port 9999\n"); udp->dest = ntohs(9998); } } } } }
return XDP_PASS;}

目标端口是9999的UDP包,“struct udphdr”结构体中端口被修改为9998。

c. 测试


先不启用XDP程序。


这里用“nc”命令开启两个监听端口,分别是9998和9999。


同样用“nc”命令发送UDP包给9999端口,可以看到现在没什么问题。
 
下面启动XDP程序再试试。


再次发送新的UDP包到端口9999。


可以看到,发送给9999端口的“tset”内容被9998端口所接收。
 
接下来可以看下tcpdump工具的抓包结果。


这时候,XDP程序会在Packet进入网络栈,也就是可以被tcpdump获取到,之前就已经完成对原Packet的修改。

那么可以假设这样的场景: 
 
 - 目标机器上存在防火墙,只允许特定端口数据包进入。这个时候可以通过上述功能的XDP程序,对被允许进入的数据包进行端口转发,将识别出来的、带有控制指令的包转发给目标恶意软件。从而绕过防火墙。   
 
 - 另一方面,通过XDP程序也可以对发送出去的Packet进行修改,同样该修改过程也无法被tcpdump这类工具所截获,这样就允许了隐蔽对外发送的功能。 
 

2. Linxu系统运行时


EBPF同样支持kprobe/tracepoint这两个Linux内核提供的函数探针功能。通过这个功能可以进行跟多有趣的操作,比如文章中给出的绕过ssh认证的例子。
 
以下实验参考 https://github.com/pathtofile/bad-bpf这个项目。

该项目提供了好几个易懂但是有效的BPF基于probe、tracepoint的恶意利用方式。
 
本节中使用项目的Sudo-Add、Pid-Hide这两个个例子。

a. Build

安装方式:https://github.com/pathtofile/bad-bpf#Build


b. 代码文件

该项目主要就是基于之前提到的libbpf+CO-RE结构。


其中Sudo-Add:
  • 用户层Loader文件为src/sudoadd.c。

  • BPF程序为src/sudoadd.bpf.c。
    程序在sys_exit_read这个处理系统调用read返回动作的时候,对“/etc/sudoers”文件进行了修改。添加了目标用户到该文件中。


另一个Pid-Hide:
  • BPF程序为src/pidhide.bpf.c。
    程序Hook了getdents64系统调用,该调用会在执行“ps”命令时候触发。这个时候,会将目标pid成进程链表上取下来。


c. 测试一


创建新用户,并且此时新用户没有在sudoers文件中,所以它无法使用“sudo”命令。


启动“sudoadd”这个EBPF程序,以新用户为指定目标。


重新执行“sudo”命令,发现执行成功,且有相应的输出。



d. 测试二


创建新进程。


启动“PidHide”这个EBPF程序,设定好Pid。


这个时候在看进程列表。


原进程被隐藏了。

通过上面两个例子,可以看出来EBPF恶意利用probe、tracepoint等系统功能时候带来的危险性。


0x02 防御


文章中提了三点:运行前、运行时和运行后。其中运行前对我们来说没什么意义,略过不谈。下面主要看下运行时检测,然后粗略看下运行后检测。

一、运行时


运行时监控主要基于“tracepoint/syscalls/sys_enter_bpf”。

EBPF程序说白了还是需要通过bpf这个系统调用才能运行,那么通过上面这个针对bpf系统调用的探针,就可以对EBPF程序的运行进行监控。

这里提供相关EBPF程序:

truct _sys_enter_bpf{    __u64 unused;    int syscall_nr;    unsigned long cmd;    unsigned long bpf_attr_addr;    unsigned long size;};

// TODO: ptrace的hook还有个pid_tree要加,已加/* bpf */SEC("tracepoint/syscalls/sys_enter_bpf")int sys_enter_bpf(struct _sys_enter_bpf *ctx){ event_data_t data = {}; if (!init_event_data(&data, ctx)) return 0; data.context.type = TRACEPOINT_SYSCALLS_BPF; unsigned long cmd; bpf_probe_read(&cmd, sizeof(unsigned long), &ctx->cmd); save_to_submit_buf(&data, &ctx->cmd, sizeof(unsigned long), 0); events_perf_submit(&data); return 0;}


经过实验,对于我们现在进行的项目来说,只需要获取到该bpf系统调用的Command信息就够了。因为在此,我们可以假定项目必然是被最先执行的。

a. 直接运行EBPF程序


运行上面的PidHide的例子:


可以看到被记录下来的命令行信息。

b. 命令行执行EBPF程序


除了执行自己编写的BPF Loader程序加载BPF字节码。还有一种是通过命令加载BPF字节码的方式,比如“ip”命令。
 
以以下这个非常简单的EBPF程序为例:
 
ip_xdp.bpf.c

直接通过clang编译成BPF字节码。
#include <linux/bpf.h>

#ifndef __section#define __section(NAME) \ __attribute__((section(NAME), used))#endif
__section("prog")int xdp_drop(struct xdp_md *ctx) { return XDP_DROP;}
char __license[] __section("license") = "GPL";

通过ip命令执行生成的BPF字节码。
CC=/usr/bin/clang-11 CXX=/usr/bin/clang++-11 clang -O2 -g -Wall -target bpf -c ip_xdp.bpf.c -o ip_xdp.bpf.o

查看被监控到的信息:


可以看到该方式也可以被正常监控。

二、运行后


bpftool工具就可以用来查看当前运行的EBPF程序的情况。


ip命令也可以查看和它自己相关的EBPF程序。



0x03 总结


就目前我们遇到的情况来看,使用运行时检测的方式无疑是一种更好的选择。而EBPF的恶意利用虽然有点明显,其实受限制颇多,且存在适配的问题。这些都是有待解决的。


参考链接:




银河实验室

银河实验室(GalaxyLab)是平安集团信息安全部下一个相对独立的安全实验室,主要从事安全技术研究和安全测试工作。团队内现在覆盖逆向、物联网、Web、Android、iOS、云平台区块链安全等多个安全方向。
官网:http://galaxylab.pingan.com.cn/

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接