WEBKT

实战eBPF:打造网络入侵检测系统(IDS),精准识别端口扫描、SQL注入与XSS攻击

19 0 0 0

什么是eBPF?

准备工作

端口扫描检测

原理

eBPF程序

代码解释

编译和加载eBPF程序

SQL注入检测

原理

eBPF程序

代码解释

编译和加载eBPF程序

跨站脚本攻击(XSS)检测

原理

eBPF程序

代码解释

编译和加载eBPF程序

总结

网络安全,一直是程序员和运维工程师们关注的焦点。传统的入侵检测系统(IDS)往往面临性能瓶颈,而新兴的eBPF技术,凭借其在内核态高效运行的特性,为我们提供了一种全新的解决方案。本文将带你一步步使用eBPF构建一个简单的IDS,能够检测常见的网络攻击,如端口扫描、SQL注入和跨站脚本攻击(XSS)。

什么是eBPF?

eBPF(extended Berkeley Packet Filter)是一种内核技术,允许用户在内核空间安全地运行自定义代码,而无需修改内核源码或加载内核模块。这意味着我们可以利用eBPF来监控和分析网络流量,而不会对系统性能造成显著影响。

eBPF程序通常由用户空间程序加载到内核中,并附加到特定的事件(例如,网络数据包到达网卡)。当事件发生时,eBPF程序会被触发执行,它可以读取事件相关的数据,并根据预定义的规则进行处理。

准备工作

在开始之前,你需要确保你的系统满足以下条件:

  • Linux内核版本 >= 4.10: eBPF的功能在不断发展,建议使用较新的内核版本。
  • 安装libbpf: libbpf是一个用户空间库,用于加载、管理和与eBPF程序交互。
  • 安装bcc: BCC(BPF Compiler Collection)是一套工具,可以简化eBPF程序的开发、编译和调试。你可以从 https://github.com/iovisor/bcc 获取更多信息。

端口扫描检测

端口扫描是一种常见的网络侦察技术,攻击者通过扫描目标主机的端口,来确定哪些端口是开放的,从而寻找潜在的漏洞。我们可以使用eBPF来检测端口扫描行为。

原理

端口扫描通常涉及在短时间内向目标主机的多个端口发送连接请求(SYN包)。我们可以通过eBPF程序记录每个源IP地址在一定时间内尝试连接的端口数量,如果超过阈值,则认为该IP地址正在进行端口扫描。

eBPF程序

以下是一个简单的eBPF程序,用于检测端口扫描:

#include <uapi/linux/bpf.h>
#include <linux/version.h>
#include <linux/tcp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define MAX_PORTS 10
#define TIME_WINDOW_NS 1000000000 // 1 second
struct pkt_key {
__u32 src_addr;
__u16 dst_port;
};
struct port_scan_data {
__u64 timestamp;
__u32 count;
};
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(key_size, sizeof(struct pkt_key));
__uint(value_size, sizeof(struct port_scan_data));
__uint(max_entries, 1024);
} port_scan_map SEC(".maps");
SEC("tracepoint/tcp/tcp_connect")
int handle_tcp_connect(void *ctx) {
struct pkt_key key = {};
struct port_scan_data *val;
__u64 now = bpf_ktime_get_ns();
struct tcp_connect_data_t {
__u32 saddr;
__u32 daddr;
__u16 sport;
__u16 dport;
int __unused__;
} *data = (struct tcp_connect_data_t *)ctx;
key.src_addr = data->saddr;
key.dst_port = bpf_ntohs(data->dport);
val = bpf_map_lookup_elem(&port_scan_map, &key);
if (val) {
if (now - val->timestamp < TIME_WINDOW_NS) {
val->count++;
if (val->count > MAX_PORTS) {
bpf_printk("Port scan detected from %x\n", key.src_addr);
}
} else {
val->timestamp = now;
val->count = 1;
}
} else {
struct port_scan_data new_val = {now, 1};
bpf_map_update_elem(&port_scan_map, &key, &new_val, BPF_ANY);
}
return 0;
}
char LICENSE[] SEC("license") = "GPL";

代码解释

  • port_scan_map: 这是一个LRU哈希表,用于存储每个源IP地址连接过的端口信息。Key是pkt_key结构体,包含源IP地址和目标端口;Value是port_scan_data结构体,包含时间戳和连接计数。
  • handle_tcp_connect: 这是一个tracepoint处理函数,它附加到tcp_connect tracepoint。当一个新的TCP连接建立时,该函数会被调用。函数首先从tracepoint上下文中提取源IP地址和目标端口,然后在port_scan_map中查找对应的记录。如果找到记录,并且距离上次连接的时间小于TIME_WINDOW_NS,则连接计数加1。如果连接计数超过MAX_PORTS,则认为该IP地址正在进行端口扫描,并打印一条消息。如果未找到记录,则创建一个新的记录,并将时间戳设置为当前时间,连接计数设置为1。
  • bpf_printk: 这是一个特殊的eBPF辅助函数,用于在内核中打印调试信息。这些信息可以通过trace命令或者/sys/kernel/debug/tracing/trace_pipe文件查看。

编译和加载eBPF程序

使用bcc编译该eBPF程序:

#!/usr/bin/env python3
from bcc import BPF
# 加载eBPF程序
b = BPF(src_file="port_scan.c")
# 附加tracepoint
b.attach_tracepoint(tp="tcp:tcp_connect", func="handle_tcp_connect")
# 打印输出
b.trace_print()

将以上代码保存为port_scan.py,并运行:

sudo python3 port_scan.py

现在,你可以尝试使用nmap或其他端口扫描工具扫描你的主机,看看是否能够检测到端口扫描行为。

SQL注入检测

SQL注入是一种常见的Web应用程序安全漏洞,攻击者通过在用户输入中插入恶意的SQL代码,来篡改或窃取数据库中的数据。我们可以使用eBPF来检测SQL注入攻击。

原理

SQL注入攻击通常涉及在HTTP请求的参数中包含特殊的SQL关键字或语法。我们可以使用eBPF程序来检查HTTP请求的参数,如果发现可疑的SQL关键字或语法,则认为该请求可能包含SQL注入攻击。

eBPF程序

以下是一个简单的eBPF程序,用于检测SQL注入:

#include <uapi/linux/bpf.h>
#include <linux/version.h>
#include <linux/string.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define MAX_DATA_SIZE 128
struct data_t {
char data[MAX_DATA_SIZE];
};
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(struct data_t));
__uint(max_entries, 1);
} data_map SEC(".maps");
// SQL Keywords to look for
const char *sql_keywords[] = {
"SELECT", "UPDATE", "DELETE", "INSERT", "DROP", "UNION", "--", ";",
};
SEC("uprobe/http_server")
int handle_http_request(void *ctx) {
int key = 0;
struct data_t *data = bpf_map_lookup_elem(&data_map, &key);
if (!data) {
return 0;
}
// Get the HTTP request data (replace with actual logic to get request data)
// This is just a placeholder. You would need to copy the actual HTTP
// request data into the `data->data` buffer.
// For example, you might read from a shared memory region or use
// kprobes to access the HTTP request buffer.
// This example simulates receiving data. In real use, this needs to be
// replaced with the actual HTTP request data.
char simulated_data[] = "GET /?id=1' UNION SELECT password FROM users -- HTTP/1.1";
strncpy(data->data, simulated_data, MAX_DATA_SIZE - 1);
data->data[MAX_DATA_SIZE - 1] = '\0';
for (int i = 0; i < sizeof(sql_keywords) / sizeof(sql_keywords[0]); i++) {
if (strstr(data->data, sql_keywords[i])) {
bpf_printk("SQL injection attempt detected: %s\n", data->data);
break;
}
}
return 0;
}
char LICENSE[] SEC("license") = "GPL";

代码解释

  • data_map: 这是一个PERCPU_ARRAY类型的Map,用于存储HTTP请求的数据。每个CPU核心都有一个独立的data_t结构体,可以避免竞争。
  • sql_keywords: 这是一个字符串数组,包含常见的SQL关键字,用于检测SQL注入攻击。
  • handle_http_request: 这是一个uprobe处理函数,它附加到HTTP服务器的处理请求的函数上。当HTTP服务器接收到一个新的请求时,该函数会被调用。函数首先从data_map中获取data_t结构体,然后将HTTP请求的数据复制到data->data缓冲区中。最后,函数遍历sql_keywords数组,如果发现data->data缓冲区中包含任何SQL关键字,则认为该请求可能包含SQL注入攻击,并打印一条消息。
  • strncpy: 用于拷贝字符串,防止缓冲区溢出

编译和加载eBPF程序

使用bcc编译该eBPF程序:

from bcc import BPF
# 加载eBPF程序
b = BPF(src_file="sql_injection.c")
# 替换为你的HTTP服务器处理请求的函数名和库
function_name = "http_process_request" # 示例函数名
library_path = "/usr/lib/libhttpserver.so" # 示例库路径
# 附加uprobe
b.attach_uprobe(name=library_path, sym=function_name, fn_name="handle_http_request")
# 打印输出
b.trace_print()

注意: 你需要将function_namelibrary_path替换为你实际的HTTP服务器处理请求的函数名和库的路径。你可以使用objdump -T命令来查找HTTP服务器的函数名。

将以上代码保存为sql_injection.py,并运行:

sudo python3 sql_injection.py

现在,你可以尝试发送包含SQL注入攻击的HTTP请求到你的Web应用程序,看看是否能够检测到SQL注入攻击。

跨站脚本攻击(XSS)检测

跨站脚本攻击(XSS)是一种常见的Web应用程序安全漏洞,攻击者通过在Web页面中插入恶意的JavaScript代码,来窃取用户的Cookie或执行其他恶意操作。我们可以使用eBPF来检测XSS攻击。

原理

XSS攻击通常涉及在HTTP请求的参数或响应中包含特殊的JavaScript代码,例如<script>标签。我们可以使用eBPF程序来检查HTTP请求的参数和响应,如果发现可疑的JavaScript代码,则认为该请求或响应可能包含XSS攻击。

eBPF程序

以下是一个简单的eBPF程序,用于检测XSS攻击:

#include <uapi/linux/bpf.h>
#include <linux/version.h>
#include <linux/string.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define MAX_DATA_SIZE 128
struct data_t {
char data[MAX_DATA_SIZE];
};
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(struct data_t));
__uint(max_entries, 1);
} data_map SEC(".maps");
// XSS patterns to look for
const char *xss_patterns[] = {
"<script", "</script", "javascript:", "onerror=",
};
SEC("uprobe/http_server")
int handle_http_request(void *ctx) {
int key = 0;
struct data_t *data = bpf_map_lookup_elem(&data_map, &key);
if (!data) {
return 0;
}
// Get the HTTP request data (replace with actual logic to get request data)
// This is just a placeholder. You would need to copy the actual HTTP
// request data into the `data->data` buffer.
// For example, you might read from a shared memory region or use
// kprobes to access the HTTP request buffer.
// This example simulates receiving data. In real use, this needs to be
// replaced with the actual HTTP request data.
char simulated_data[] = "GET /?name=<script>alert('XSS')</script> HTTP/1.1";
strncpy(data->data, simulated_data, MAX_DATA_SIZE - 1);
data->data[MAX_DATA_SIZE - 1] = '\0';
for (int i = 0; i < sizeof(xss_patterns) / sizeof(xss_patterns[0]); i++) {
if (strstr(data->data, xss_patterns[i])) {
bpf_printk("XSS attempt detected: %s\n", data->data);
break;
}
}
return 0;
}
char LICENSE[] SEC("license") = "GPL";

代码解释

  • data_map: 这是一个PERCPU_ARRAY类型的Map,用于存储HTTP请求的数据。每个CPU核心都有一个独立的data_t结构体,可以避免竞争。
  • xss_patterns: 这是一个字符串数组,包含常见的XSS模式,用于检测XSS攻击。
  • handle_http_request: 这是一个uprobe处理函数,它附加到HTTP服务器的处理请求的函数上。当HTTP服务器接收到一个新的请求时,该函数会被调用。函数首先从data_map中获取data_t结构体,然后将HTTP请求的数据复制到data->data缓冲区中。最后,函数遍历xss_patterns数组,如果发现data->data缓冲区中包含任何XSS模式,则认为该请求可能包含XSS攻击,并打印一条消息。

编译和加载eBPF程序

使用bcc编译该eBPF程序:

from bcc import BPF
# 加载eBPF程序
b = BPF(src_file="xss_detection.c")
# 替换为你的HTTP服务器处理请求的函数名和库
function_name = "http_process_request" # 示例函数名
library_path = "/usr/lib/libhttpserver.so" # 示例库路径
# 附加uprobe
b.attach_uprobe(name=library_path, sym=function_name, fn_name="handle_http_request")
# 打印输出
b.trace_print()

注意: 你需要将function_namelibrary_path替换为你实际的HTTP服务器处理请求的函数名和库的路径。你可以使用objdump -T命令来查找HTTP服务器的函数名。

将以上代码保存为xss_detection.py,并运行:

sudo python3 xss_detection.py

现在,你可以尝试发送包含XSS攻击的HTTP请求到你的Web应用程序,看看是否能够检测到XSS攻击。

总结

本文介绍了如何使用eBPF构建一个简单的入侵检测系统(IDS),能够检测常见的网络攻击,如端口扫描、SQL注入和跨站脚本攻击(XSS)。

通过使用eBPF,我们可以实现高性能的网络安全监控,而不会对系统性能造成显著影响。当然,本文提供的只是一个基础的示例,你可以根据实际需求,扩展和完善这个IDS,例如:

  • 支持更多的攻击模式: 例如,DDoS攻击、恶意软件传播等。
  • 使用更复杂的检测规则: 例如,基于正则表达式的模式匹配。
  • 集成到现有的安全系统中: 例如,与SIEM系统集成,实现集中化的安全管理。
  • 添加自动响应机制: 例如,自动阻止恶意IP地址的访问。

eBPF在网络安全领域有着广阔的应用前景,相信随着技术的不断发展,eBPF将会在未来的网络安全中扮演越来越重要的角色。

友情提示: 本文提供的代码示例仅用于演示目的,请勿直接用于生产环境。在实际应用中,你需要根据实际情况进行修改和完善,并进行充分的测试。

参考资料:

安全小黑 eBPFIDS网络安全

评论点评

打赏赞助
sponsor

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

分享

QRcode

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