跳转到主要内容

实践中,哪些技术边界真正约束恶意的 eBPF 载荷?

简短回答: 这些边界大多不是人们默认以为的那样。验证器(verifier)是内存安全检查,而不是行为安全检查:它(在没有验证器漏洞的前提下)只能证明程序不会破坏内核内存,对程序是否具有恶意意图一无所知。实践中,恶意载荷受到四个相互独立、可以测试的边界的约束,每个边界都有已知的失效模式:

  1. 加载门禁 —— 哪些主体(principal)可以挂载程序(capabilities、unprivileged-BPF sysctl、用户命名空间)。
  2. 验证器的内存安全范围 —— 已加载程序对内存可以做什么;这个保证随每个内核版本移动,且在不同架构上的实际表现不同。
  3. 可移植性边界(BTF/CO-RE) —— 假设特定内核结构体布局的载荷,只有在目标内核有 BTF 且布局匹配时才能工作。
  4. 可见性边界 —— 已加载的程序在进程列表、syslog、auditd 中不可见,只能通过 BPF 原生遥测观测。

如果你在为一个实证研究定范围(或者在为蓝队构建检测基线),要映射的就是这四个边界。"验证器会阻止激进的狀態操縱"只是边界 2,而且是其中最窄的一个:能通过验证器的载荷已经通过了唯一的静态关卡。之后的所有事情都是能力、可移植性和可观测性——而不是静态分析。

为什么"验证器放行"不等于"安全"

内核的信任模型把验证器当作唯一的静态关卡:程序在被验证之前是不可信字节码,验证通过后 JIT 编译执行,它调用的 helper 不会重新检查内存安全。从已验证程序到内核的信任链,只有验证器本身那么强。一篇 2025 年的系统性分析(SoK,USENIX Security)把验证器的弱点分为三类,每一类都有真实 CVE 佐证:

  • 不健全的检查 —— 路径剪枝等优化依赖精确的寄存器依赖跟踪;漏掉一个依赖,不安全路径就可能被当成安全路径剪掉(CVE-2023-2163)。
  • 不完备的检查 —— 原型里没有 size 参数的 helper 会完全跳过 size 校验(CVE-2021-4204,经由 bpf_ringbuf_submit);验证器信任 helper,而 helper 不会复核。
  • 范围有限 —— 检查止步于字节码;helper 内部的时间安全(temporal-safety)问题(例如通过 dev_map_lookup_elem 的 use-before-initialization)对验证器不可见。

同一项研究发现,公开 eBPF 程序内存操作的 1.62–3.74%(37–85 个)无法被证明全面内存安全——这些是普通工具而非恶意代码,说明验证目前就留下了这么多未证明的部分。再加上验证器持续不断的 CVE 流(近年来每年数十个,自 4.4 内核引入 eBPF 以来已有数百个模糊测试报告的内存错误 bug),实际结论是:内存安全边界随每个内核版本移动。一个版本上被拒绝的程序,可能在另一个版本上成功加载。

这并不说明验证器日常很弱——对合法程序来说,内存破坏才是真实风险,验证器就是为防它而存在的。它说明"验证器放行"是比"安全"更窄的性质,防御者不应把两者混为一谈。

边界 1:加载门禁

约束谁能加载程序的是能力策略,不是验证器:

  • 现代发行版默认禁用非特权 BPF 加载(kernel.unprivileged_bpf_disabled=1)。历史上本地提权正是从这里进来的:在 CVE-2016-4557 / CVE-2018-18445 时代,非特权字节码注入与验证器越界绕过就是入口。
  • 自 Linux 5.8 起,CAP_BPF 把 BPF 加载/挂载与 CAP_SYS_ADMIN 分开,只持有 CAP_SYS_ADMIN 的服务不再自动拥有 BPF,反之亦然。
  • 启用 BPF LSM 时,加载/挂载/pin 可以受 MAC 策略约束。

公开 eBPF rootkit(从 Black Hat USA 2021 "With Friends Like eBPF" 的工作及其开源后代)到首个有文档记录的生产环境 eBPF 后门,经验模式是同一个:这些是驻留与隐蔽技术,不是入口技术。 行为主体已经持有 root 或 CAP_SYS_ADMIN;eBPF 被用来保持隐藏、通过 pin 住的 map 驻留、并把命令路由进数据路径——全部在验证器放行行为之内。加载门禁回答的是"谁",而不是"进去之后做什么"。

边界 2:验证器范围,按版本与架构

做 x86_64 对比 ARM64 矩阵时,要精确区分什么真正不同:

  • 验证器是共享 C 代码;对同一程序,它不会在 ARM64 上"更严格"。
  • JIT 是按架构的,架构相关的 bug 历来出现在 JIT 路径和按架构的 selftest 覆盖上。2026 年 bpf 邮件列表上一个活跃的系列正是关于此事:修复 powerpc JIT 处理大型 BPF 程序时的缓冲区溢出,以及一个明确针对该架构缺失的验证器 selftest 覆盖的补丁。这种按架构的验证与测试欠账,正是跨架构研究应该测量的东西。
  • 内核配置差异(启用的 BPF 特性、CONFIG_* 选项、BTF 可用性)会改变同版本两个内核上能加载什么。

所以测量单位不是单独的"架构"或"版本",而是四元组**(架构、内核版本、内核配置、BTF 可用性)**。一个在带完整 BTF 的 x86_64 上能加载的载荷,可能在同版本 ARM64 上失败——或者能加载,却被架构相关的检查拒绝。两种结果都是边界数据。

边界 3:可移植性边界(BTF/CO-RE)

CO-RE 是让载荷更不可移植的边界,而不是相反。基于某个内核 BTF 构建的程序携带重定位(relocation),必须在目标内核的 BTF 上解析:

  • 目标内核没有 BTF(被裁剪或未编译)→ CO-RE 程序根本无法加载。
  • 源与目标之间结构体布局漂移 → 重定位失败,或者在类型兼容但字段移位时静默产生错误偏移。
  • 内核 ABI 本身在移动:一个评审中的大型补丁系列为 BPF 引入按值结构体和 __int128 参数(包括 kfunc),载荷能表达的东西随版本变化。

对防御研究来说这也是检测边界:BTF 不匹配导致的加载失败会在加载路径上留下签名(BPF_PROG_LOAD 因重定位失败),加载监控可以把它标记出来。对攻击研究来说,它是可移植性天花板:手工调参、非 CO-RE 的偏移是绕开 BTF 要求的唯一方式,而这条路径可移植性差得多,也更可能触发验证器越界检查。

边界 4:可见性边界

大多数防御真正失效的是这个边界,也是近期公开研究反复回到的一点:

  • eBPF 程序不出现在 /proc 中,其活动也不出现在 syslog 或 auditd 中。被入侵的主机可以运行敌对内核逻辑,而常规遥测看起来毫不起眼。
  • /sys/fs/bpf 下的 pin 住 map 提供跨程序卸载的驻留能力。
  • 首个有文档记录的生产环境 eBPF 后门(BPFDoor)完全运行在验证器放行行为之内:数据路径 hook 作为命令通道,pin 住的 map 实现驻留,进程表里什么都没有。
  • 威胁情报研究现在把 eBPF 植入体归类为独立的 Linux 恶意软件类别,原因正是它们在验证器放行边界之内运行。

唯一可靠的可见性是 BPF 原生的:bpftool prog show / bpftool map show、列 /sys/fs/bpf,以及监视 BPF_PROG_LOAD / BPF_MAP_CREATE / 挂载事件的运行时监控(Falco 规则、Tracee、eBPFmon 类工具)。如果检测栈无法把 BPF 加载/挂载事件与身份和意图关联起来,边界 4 就是开放的,其他三个边界的重要性都会下降。

如何测量这些边界(可复现的实验设计)

如果目标是可复现的技术矩阵,把它结构化为五个独立轴,每个轴都能用公开工具测试:

  1. 加载门禁矩阵 —— 主体(非特权、仅 CAP_BPF、仅 CAP_SYS_ADMIN、root、用户命名空间 + CAP_BPF)× 发行版/内核配置,用同一个最小程序记录 BPF_PROG_LOAD 的接受情况。
  2. 按 (arch, version, config) 的验证器边界 —— 在每个目标上跑公开的 CVE 复现集(模糊测试复现程序、selftests/bpf);记录哪些验证器拒绝、接受或崩溃。"验证器反制"在这里才可测量。
  3. CO-RE 可移植性 —— 基于 N 套内核 BTF 构建同一个 CO-RE 程序;统计每个目标的成功/失败加载,至少包含一个禁用 BTF 的目标。
  4. 性能代价 —— 按架构基准测试 syscall 拦截与重 map 工作负载;它同时是"规避代价"数字和检测信号数字,因为同一基线可以标记异常。
  5. 可见性缺口 —— 同一工作负载下,对比 /proc + syslog + auditd 与 bpftool 各能看到什么,量化差异。

轴 1、4、5 完全不需要攻击载荷:它们用合法程序测量环境。轴 2 用的是已经公开多年的复现程序。这样工作保持在防御研究范围内,同时产出蓝队真正缺少的矩阵。

答案的适用边界

  • 这些是 Linux 内核的边界。其他运行时上的 eBPF(用户态解释器、Wasm-BPF、非 Linux 移植)的保证集合不同且往往更弱。
  • 矩阵的保鲜期取决于被测内核:验证器行为、发行版默认值、能力划分都会随版本变化,所以要重新测量而不是引用旧结果。
  • "通过验证"是内存安全陈述,不是安全陈述。已验证程序仍可以经 map 外泄内核数据、挂载到特权 hook、或隐藏自己——这正是边界 4 存在的原因。
  • 历史上的非特权 BPF 攻击时代在现行发行版上已基本关闭(默认禁用非特权加载);现代威胁模型应假设 post-root 的驻留与隐蔽,而非非特权入口。

参考资料

  • Linux 内核文档:eBPF verifier
  • Huang 等,"SoK: Challenges and Paths Toward Memory Safety for eBPF",USENIX Security 2025 — 论文
  • Linux Security,"Linux eBPF Security Advisory: Critical Visibility Concerns 2025:0011-2" — 文章
  • "With Friends Like eBPF, Who Needs Enemies?",Black Hat USA 2021 — 幻灯片
  • Red Canary,"eBPF Malware" 威胁检测分析 — 文章
  • eBPF 基金会,"Threat model and independent verifier audit" — 帖子
  • eBPF.io,"What is eBPF?"(内核加固措施) — 帖子

当日社区讨论

本次运行覆盖情况:两个启用存档的 Slack 工作区中有一个可读——一个公开频道,严格 24 小时窗口内零条消息,7 天回退窗口内 7 条消息。另一个已启用存档的工作区,其两个白名单频道不存在于存档中,记录为不可访问而非安静。两个白名单 Discord 工作区是仅可视浏览器的来源,本次运行没有可用的可视浏览器会话,此处报告为覆盖缺口而非沉默。公开的 bpf@vger.kernel.org 存档与公开从业者论坛通过其普通公开页面完成审阅。

一个反复出现的问题:恶意 eBPF 到底在哪里碰壁?

窗口内最有分量的讨论是一个研究定范围的交流,它同时出现在可读存档频道和公开从业者论坛中:对"限制恶意 eBPF 载荷的技术边界"做实证研究——验证器反制、CO-RE 局限、以及跨 x86_64 与 ARM64 的内核操作性能代价——对防御社区是否有价值,从哪里入手。上面发布的回答就是对这个问题的回应:验证器只是四个边界之一,而且是最窄的一个;研究应围绕加载门禁、按 (arch, version, config) 的验证器行为、BTF/CO-RE 可移植性、性能代价、可见性缺口来组织。同一个问题在两个独立社区中浮现,说明这是领域公开文献中真实反复出现的缺口,而不是一次性提问。

内核邮件列表正在修补同一个边界

公开内核存档今天恰好活跃在这一主题上。一个多补丁系列修复了 powerpc JIT 处理大型 BPF 程序时的缓冲区溢出,并明确针对该架构缺失的验证器 selftest 覆盖——这是按架构的验证与测试欠账仍然存在的直接证据,也是跨架构研究应当测量的边界。一个 RFC 为指令集提出 UHMUL 与 SHMUL 乘法指令;另一个 RFC 提出跨低 32 位跟踪标量相等性,延续验证器标量范围跟踪的活跃工作。一个大型补丁系列为 BPF 引入按值结构体与 __int128 参数(包括 kfunc),一个 bpf-next 系列通过新 kfunc 加入 BPF 驱动的主动 memcg 回收。一个 sockmap 修复处理了自重定向序列号的重复计数。共同线索:验证器与其 ABI 的演化快于文档和下游假设,这正是边界矩阵需要版本轴的原因。

从业者论坛:性能与行为重构

公开论坛这一周以生产工程为主,而非安全。一个两部分的公开重构系列——第二部分详述某大型运营方用 eBPF 填补的 Linux 生态十一个缺口——继续发布在基金会博客上。一个进程行为重构工具把原始 syscall 事件关联成行为时间线;两个帖子分别讨论从 eBPF 榨取性能、以及让 BPF 策略又快又省空间。一位 CNI 作者分享了 ClusterIP 主机路由数据路径,其第一个排障步骤就是反向路径过滤——与 2026-09-03 回答 探讨的同一边界。这些帖子反复出现的期待:量化指令与数据路径代价,并证明低层事件可以关联成可复现的解释。这也是上面安全边界在运维上的教训:性能基线与 BPF 原生可见性基线,是边界 4 可测量的前提。

继续阅读

最后更新
2026年9月8日
首次发布
2026年9月8日
贡献者
Yunwei 123

这个页面有帮助吗?