WEBKT

安全工程师视角:如何用eBPF揪出服务器里的“内鬼”?

62 0 0 0

作为一名安全工程师,每天和病毒、木马这些“不速之客”打交道是家常便饭。传统的恶意代码检测方法,比如基于特征的扫描,往往滞后于新型威胁的出现,而且容易被各种加壳、混淆技术绕过。有没有一种更“聪明”的方法,能够实时监控服务器行为,揪出那些隐藏很深的“内鬼”呢?

答案是肯定的,那就是eBPF(extended Berkeley Packet Filter)。

什么是eBPF?

简单来说,eBPF就像一个“探针”,可以插入到Linux内核的各种关键位置,比如系统调用、函数入口/出口、网络事件等等。通过编写eBPF程序,我们可以hook这些事件,实时地观察和分析系统行为,而无需修改内核源码或重启服务器。

与传统的内核模块相比,eBPF程序运行在受限的沙箱环境中,避免了因bug或恶意代码导致内核崩溃的风险。同时,eBPF程序经过严格的验证,确保其安全性和效率。

eBPF在恶意代码检测中的优势

  1. 实时性:eBPF程序可以实时地监控系统行为,一旦发现可疑活动,立即发出警报,大大缩短了响应时间。

  2. 灵活性:我们可以根据实际需求,编写各种eBPF程序,监控不同的系统事件,实现定制化的恶意代码检测。

  3. 低开销:eBPF程序运行在内核态,效率很高,对系统性能的影响很小。

  4. 无需修改内核:eBPF程序可以在不修改内核源码的情况下,实现内核级别的监控,避免了潜在的兼容性问题。

如何使用eBPF检测恶意代码?

下面,我将分享几个使用eBPF检测恶意代码的实用技巧和案例:

1. 监控可疑的系统调用

恶意代码通常会调用一些敏感的系统调用,比如execve(执行新程序)、open(打开文件)、connect(建立网络连接)等。我们可以编写eBPF程序,监控这些系统调用的参数和返回值,分析其行为是否异常。

例如,我们可以监控execve系统调用,如果发现有程序从/tmp/var/tmp等临时目录执行,很可能就是恶意代码在作祟。

// 监控execve系统调用的eBPF程序
#include <linux/sched.h>
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
char filename[256];
};
BPF_PERF_OUTPUT(events);
int kprobe__do_execve(struct pt_regs *ctx, const char *filename)
{
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
bpf_probe_read_str(data.filename, sizeof(data.filename), (void *)filename);
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}

这个eBPF程序hook了do_execve函数(execve系统调用的内核实现),记录下进程ID、时间戳、进程名和执行的文件名,并通过perf事件将数据发送到用户空间。在用户空间,我们可以编写程序分析这些数据,找出可疑的execve调用。

2. 检测异常的网络连接

恶意代码通常会建立网络连接,与远程服务器通信,以下载恶意payload或上传窃取的数据。我们可以编写eBPF程序,监控connect系统调用,分析其连接的目标IP地址和端口是否可疑。

例如,我们可以维护一个恶意IP地址和端口的黑名单,如果发现有程序尝试连接黑名单中的地址,立即发出警报。

// 监控connect系统调用的eBPF程序
#include <linux/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
u32 ip;
u16 port;
};
BPF_PERF_OUTPUT(events);
int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk)
{
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
struct inet_sock *inet = inet_sk(sk);
data.ip = inet->daddr;
data.port = ntohs(inet->dport);
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}

这个eBPF程序hook了tcp_v4_connect函数(TCP IPv4连接的内核实现),记录下进程ID、时间戳、进程名、目标IP地址和端口,并通过perf事件将数据发送到用户空间。在用户空间,我们可以编写程序分析这些数据,检查目标IP地址和端口是否在黑名单中。

3. 监控文件访问行为

恶意代码通常会修改或删除系统文件,以达到破坏或隐藏自己的目的。我们可以编写eBPF程序,监控openwriteunlink等文件操作相关的系统调用,分析其访问的文件路径和操作类型是否可疑。

例如,我们可以监控对/etc/passwd/etc/shadow等敏感文件的写操作,如果发现有程序尝试修改这些文件,立即发出警报。

// 监控open系统调用的eBPF程序
#include <linux/fs.h>
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
char filename[256];
int flags;
};
BPF_PERF_OUTPUT(events);
int kprobe__vfs_open(struct pt_regs *ctx, const struct path *path, struct file *file, int flags)
{
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
bpf_probe_read_str(data.filename, sizeof(data.filename), path->dentry->d_name.name);
data.flags = flags;
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}

这个eBPF程序hook了vfs_open函数(文件打开的内核实现),记录下进程ID、时间戳、进程名、文件名和打开标志,并通过perf事件将数据发送到用户空间。在用户空间,我们可以编写程序分析这些数据,检查访问的文件是否是敏感文件,以及打开标志是否可疑。

4. 基于行为模式的检测

除了监控单个系统调用外,我们还可以将多个系统调用组合起来,分析其行为模式是否符合恶意代码的特征。

例如,一个典型的恶意代码行为模式是:

  • 从网络下载恶意payload
  • 将payload写入到临时文件
  • 执行临时文件

我们可以编写eBPF程序,同时监控connectwriteexecve系统调用,如果发现这三个系统调用在短时间内连续发生,并且符合上述顺序,很可能就是恶意代码在执行恶意payload。

5. 使用机器学习辅助检测

我们可以将eBPF与机器学习结合起来,训练一个恶意代码检测模型。首先,使用eBPF程序收集大量的系统行为数据,然后将这些数据作为训练集,训练一个机器学习模型。最后,使用训练好的模型,对实时的系统行为数据进行分析,判断其是否为恶意代码。

例如,我们可以使用eBPF程序收集进程的系统调用序列,然后使用循环神经网络(RNN)训练一个恶意代码检测模型。RNN可以学习系统调用序列中的时间依赖关系,从而更准确地识别恶意代码。

eBPF恶意代码检测的实践案例

  • Cilium Tetragon:Cilium Tetragon是一个基于eBPF的安全可观测性工具,可以监控Kubernetes集群中的网络流量、进程行为和文件访问,检测恶意代码和安全漏洞。
  • Falco:Falco是一个云原生运行时安全工具,使用eBPF监控系统调用,检测异常行为,例如未授权的文件访问、特权提升和容器逃逸。
  • Tracee:Tracee是一个Linux运行时安全和跟踪工具,使用eBPF监控系统事件,提供进程、文件、网络和内核的详细信息,帮助安全分析师检测和分析恶意代码。

总结与展望

eBPF为恶意代码检测提供了一种全新的思路。它具有实时性、灵活性、低开销和无需修改内核等优点,可以有效地检测各种恶意代码,提高服务器的安全性。

当然,eBPF也存在一些挑战,比如:

  • 学习曲线:eBPF编程需要一定的内核知识和编程经验,学习曲线较陡峭。
  • 安全性:虽然eBPF程序运行在沙箱环境中,但仍然存在潜在的安全风险,需要进行严格的验证和测试。
  • 可移植性:不同的Linux内核版本可能存在差异,需要针对不同的内核版本编写不同的eBPF程序。

未来,随着eBPF技术的不断发展,相信这些挑战会逐渐得到解决。eBPF将在恶意代码检测领域发挥越来越重要的作用,为我们的服务器安全保驾护航。

安全老司机 eBPF恶意代码检测安全工程师

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/9226