WEBKT

利用 eBPF 监控系统调用,揪出潜藏的恶意行为:实战指南

20 0 0 0

1. eBPF 简介:内核观测的瑞士军刀

2. 系统调用监控:安全的第一道防线

3. 使用 eBPF 监控系统调用的基本步骤

4. 实战演练:监控 execve 系统调用

4.1 使用 bcc 编写 eBPF 程序

4.2 运行 eBPF 程序

4.3 改进:过滤和告警

5. 其他应用场景

6. 总结与展望

在云原生安全领域,eBPF (extended Berkeley Packet Filter) 已经成为一个炙手可热的技术。它允许我们在内核中安全地运行自定义代码,而无需修改内核源码或加载内核模块。这为系统监控、性能分析、安全审计等任务提供了前所未有的灵活性和效率。本文将聚焦于如何利用 eBPF 监控系统调用,检测潜在的恶意行为,例如尝试修改关键系统文件或执行特权操作。

1. eBPF 简介:内核观测的瑞士军刀

eBPF 最初设计用于网络数据包过滤,但现在已经发展成为一个通用的内核观测和控制框架。它的核心思想是在内核中注入一段用户定义的代码(eBPF 程序),这段代码可以挂载到各种内核事件上,例如系统调用、函数入口/出口、网络事件等。当这些事件发生时,eBPF 程序会被触发执行,收集相关信息并进行处理。

eBPF 的优势在于:

  • 安全: eBPF 程序在执行前会经过内核验证器的严格检查,确保其不会导致系统崩溃或安全漏洞。
  • 高效: eBPF 程序直接在内核中运行,避免了用户态和内核态之间的频繁切换,性能损耗极低。
  • 灵活: eBPF 提供了丰富的 API 和工具链,允许开发者根据自己的需求定制监控和分析逻辑。

2. 系统调用监控:安全的第一道防线

系统调用是用户态程序与内核交互的唯一途径。几乎所有的操作,例如文件读写、网络通信、进程管理等,都需要通过系统调用来完成。因此,监控系统调用是检测恶意行为的重要手段。

常见的恶意行为,例如:

  • 提权攻击: 攻击者可能会尝试利用漏洞执行 setuidsetcap 等系统调用,以获取更高的权限。
  • 文件篡改: 攻击者可能会尝试修改 /etc/passwd/etc/shadow 等关键系统文件,以控制系统。
  • 恶意进程创建: 攻击者可能会创建隐藏的后门进程,例如使用 clonefork 系统调用。

通过监控这些系统调用,我们可以及时发现并阻止这些恶意行为。

3. 使用 eBPF 监控系统调用的基本步骤

使用 eBPF 监控系统调用通常需要以下几个步骤:

  1. 选择合适的 eBPF 框架: 目前有很多 eBPF 框架可供选择,例如 bcclibbpfbpftrace 等。bcc 提供了 Python 封装,易于上手,适合快速原型开发。libbpf 是一个 C 语言库,提供了更底层的 API,性能更高。bpftrace 则是一个高级的动态追踪工具,使用类似 awk 的语法,可以方便地编写 eBPF 程序。
  2. 编写 eBPF 程序: eBPF 程序通常使用 C 语言编写,并使用特定的编译器(例如 clang)编译成 BPF 字节码。程序需要定义挂载点(例如系统调用的入口或出口),以及在事件发生时要执行的逻辑。
  3. 加载和运行 eBPF 程序: 使用 eBPF 框架提供的 API 将 BPF 字节码加载到内核中,并将其挂载到相应的事件上。程序开始运行后,会持续监控系统调用,并在事件发生时执行相应的逻辑。
  4. 收集和分析数据: eBPF 程序可以将收集到的数据存储在 BPF map 中,用户态程序可以读取这些数据进行分析和展示。例如,我们可以统计每个进程的系统调用次数,或者记录特定系统调用的参数。

4. 实战演练:监控 execve 系统调用

execve 系统调用用于执行一个新的程序。监控 execve 可以帮助我们发现潜在的恶意进程启动行为。

4.1 使用 bcc 编写 eBPF 程序

以下是一个使用 bcc 编写的 eBPF 程序,用于监控 execve 系统调用:

from bcc import BPF
# 定义 eBPF 程序
program = '''
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
char filename[128];
};
BPF_PERF_OUTPUT(events);
int kprobe__sys_execve(struct pt_regs *ctx, const char *filename, const char *const *argv, const char *const *envp) {
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_user_str(&data.filename, sizeof(data.filename), (void *)filename);
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
'''
# 创建 BPF 实例
bpf = BPF(text=program)
# 定义回调函数,用于处理 eBPF 程序输出的数据
def print_event(cpu, data, size):
event = bpf['events'].event(data)
print(f'{event.pid} {event.comm.decode()} {event.filename.decode()}')
# 绑定回调函数到 perf buffer
bpf['events'].open_perf_buffer(print_event)
# 循环读取 perf buffer
while True:
try:
bpf.perf_buffer_poll()
except KeyboardInterrupt:
exit()

代码解释:

  • #include <uapi/linux/ptrace.h>#include <linux/sched.h> 包含了 eBPF 程序所需的头文件。
  • struct data_t 定义了要收集的数据结构,包括进程 ID、时间戳、进程名和执行的文件名。
  • BPF_PERF_OUTPUT(events) 定义了一个 perf buffer,用于将数据从内核态传递到用户态。
  • kprobe__sys_execve 是一个 kprobe,它会在 sys_execve 系统调用入口处被触发执行。struct pt_regs *ctx 包含了系统调用的寄存器信息,我们可以通过它来访问系统调用的参数。
  • bpf_get_current_pid_tgid() 获取当前进程的 PID。
  • bpf_ktime_get_ns() 获取当前时间戳(纳秒)。
  • bpf_get_current_comm(&data.comm, sizeof(data.comm)) 获取当前进程的进程名。
  • bpf_probe_read_user_str(&data.filename, sizeof(data.filename), (void *)filename) 从用户态读取文件名。
  • events.perf_submit(ctx, &data, sizeof(data)) 将数据提交到 perf buffer。
  • print_event 是一个回调函数,它会在用户态被调用,用于处理 eBPF 程序输出的数据。
  • bpf['events'].open_perf_buffer(print_event) 将回调函数绑定到 perf buffer。
  • bpf.perf_buffer_poll() 循环读取 perf buffer,并调用回调函数处理数据。

4.2 运行 eBPF 程序

将上面的代码保存为 execve_monitor.py,然后使用 root 权限运行:

sudo python3 execve_monitor.py

运行后,每当有进程执行 execve 系统调用时,就会在终端输出相关信息,例如:

1234 bash /bin/ls
5678 python3 /usr/bin/python3

4.3 改进:过滤和告警

上面的程序只是简单地输出了所有 execve 系统调用的信息。在实际应用中,我们需要根据预定义的规则进行过滤和告警。

例如,我们可以添加一个黑名单,禁止执行特定的程序:

# 黑名单
blacklist = ['/bin/bash', '/usr/bin/nc']
# 定义回调函数,用于处理 eBPF 程序输出的数据
def print_event(cpu, data, size):
event = bpf['events'].event(data)
filename = event.filename.decode()
if filename in blacklist:
print(f'[ALERT] Suspicious program executed: {event.pid} {event.comm.decode()} {filename}')
else:
print(f'{event.pid} {event.comm.decode()} {filename}')

修改后的程序会在执行黑名单中的程序时输出告警信息。

5. 其他应用场景

除了监控 execve 系统调用,eBPF 还可以用于监控其他系统调用,例如:

  • open: 监控文件打开操作,可以发现恶意文件访问行为。
  • connect: 监控网络连接操作,可以发现恶意网络通信行为。
  • mmap: 监控内存映射操作,可以发现恶意代码注入行为。
  • ptrace: 监控进程跟踪操作,可以发现恶意调试行为。

通过组合不同的 eBPF 程序和分析规则,我们可以构建强大的安全监控系统。

6. 总结与展望

eBPF 为系统安全监控提供了一种全新的方式。它具有安全、高效、灵活等优点,可以帮助我们及时发现并阻止恶意行为。虽然 eBPF 的学习曲线相对陡峭,但随着相关工具和框架的不断完善,相信它会在安全领域发挥越来越重要的作用。

未来,eBPF 将会更加智能化和自动化。例如,我们可以使用机器学习算法来分析 eBPF 程序收集到的数据,自动识别异常行为,并根据预定义的策略进行响应。同时,eBPF 也会与其他安全技术(例如容器安全、云安全)更好地集成,构建更加全面的安全防护体系。

参考资料:

安全小黑 eBPF系统调用监控恶意行为检测

评论点评

打赏赞助
sponsor

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

分享

QRcode

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