先说结论
2026 年 4 月 29 日,一个 Linux 内核本地提权漏洞被公开披露,代号"Copy Fail",编号 CVE-2026-31431。Cloudflare 的安全团队和工程团队在漏洞披露后立即开始评估。最终结论是:Cloudflare 环境未受任何影响,客户数据没有风险,服务没有中断。
这篇博客的价值,在于完整记录了从漏洞披露到全面防护,48 小时内发生的所有事情------既包括漏洞本身的技术细节,也包括应急响应的每一个决策节点。
漏洞背景:内核加密模块里藏了 9 年的定时炸弹
Cloudflare 的内核更新节奏
Cloudflare 运营着遍布全球 330 个城市的 Linux 服务器基础设施,维护基于 Linux 长期支持(LTS)版本的自定义内核。社区定期合并安全和稳定性更新,这会触发一个自动化任务,大约每周生成一个新的内部内核构建版本。构建完成后,经过预生产数据中心测试,再通过"边缘重启发布(ERR)"流水线,按四周为一个周期对边缘基础设施进行系统性更新和重启。
通常情况下,当一个 CVE 被公开披露时,所需的修复补丁已经在稳定的 Linux LTS 版本中集成了数周,Cloudflare 的既有流程已经完成了部署。但"Copy Fail"是个例外------漏洞披露时,主线修复已经存在一个月,但尚未被回移(backport)到 Cloudflare 主要使用的 6.12 LTS 版本。
这意味着:按照正常流程,Cloudflare 大量服务器在漏洞披露的那一刻,仍处于暴露状态。
漏洞原理:一次 4 字节越界写入是如何造成提权的?
这个漏洞的技术链条相当精巧,涉及三个相互独立的内核机制,缺一不可。
第一层:AF_ALG------用户态的内核加密通道
Linux 内核的内部加密 API 管理 kTLS、IPsec 等功能。用户态程序通过 AF_ALG 套接字族访问这套 API,允许非特权进程请求加密或解密操作。algif_aead 模块专门用于处理带关联数据的认证加密(AEAD)算法。
简单来说,就是操作系统提供了一个接口,让普通程序也能使用内核里的加密能力------这个接口,就是这次漏洞的入口。
第二层:页缓存------操作系统的全局文件共享内存
页缓存(Page Cache)是操作系统用于缓存文件内容的共享系统缓存。修改一个属于 setuid 二进制文件的页,实际上就是在为所有用户编辑该程序,直到这个页被驱逐为止。
这意味着:如果某个攻击者能够修改 /usr/bin/su 这类 setuid-root 程序在内存中的缓存副本,那么下一次任何用户运行这个程序时,执行的就是被篡改过的版本。
第三层:越界写入------2017 年优化留下的后门
内核加密 API 使用 scatterlist(一种将多个内存页链接在一起的数据结构)。2017 年,algif_aead 模块为了性能优化,实现了就地操作,把目标页和引用页链接在一起。这个设计缺乏约束,无法阻止算法向预期边界之外写入数据。
当用户调用 recvmsg() 时,内核中的 authencesn 包装器会在合法输出区域之外执行一次 4 字节写入:
c
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
通过 splice() 系统调用,攻击者可以将目标文件的页缓存页链接到 scatterlist 中。越界写入随后会污染缓存中的文件,攻击者可以控制被修改的文件、偏移量,以及具体写入的 4 个字节------通过 sendmsg() 中的 AAD 字节 4-7 来控制写入的内容。
完整的利用链:五步拿到 root
默认的攻击目标是 /usr/bin/su,这是一个几乎每个 Linux 发行版都有的 setuid-root 二进制文件。
攻击分五步完成:
第一步,填充页缓存 :以只读方式打开 /usr/bin/su 并读取,将其内容载入内核页缓存,再通过 splice() 把这些页缓存引用传入加密的 scatterlist 中。
第二步,建立 AF_ALG 套接字 :创建 AF_ALG 套接字,绑定到 authencesn(hmac(sha256),cbc(aes)) 算法,设置密钥,接受请求套接字------全程无需任何特权。
第三步,构造写入内容 :对每 4 字节的 shellcode,通过 sendmsg() 控制写入内容,通过 splice() 控制写入位置。
第四步,触发越界写入 :调用 recvmsg() 触发解密操作。authencesn 将其暂存数据写入 /usr/bin/su 在页缓存中的目标偏移位置。虽然函数返回 -EBADMSG,但 4 字节已经写入全局页缓存。
第五步,执行 :运行 execve("/usr/bin/su") 加载被污染的页缓存。由于这个二进制文件是 setuid-root 的,注入的 shellcode 以 root 权限执行。
Cloudflare 的应急响应:五条工作线并行推进
漏洞披露后,Cloudflare 的安全和工程团队立即启动了多条并行工作线:评估暴露范围、验证检测覆盖、主动溯源威胁狩猎、工程缓解,以及继续推进软件更新。
工作线一:验证现有检测能否识别这次攻击
安全团队做的第一件事,是确认现有的端点检测能否捕获这个漏洞的利用行为。Cloudflare 的服务器运行行为检测系统,持续监控进程执行模式------它不依赖已知漏洞的签名,而是基于整个机队的行为模式判断异常。
当工程师在内部授权的验证测试中复现漏洞时,检测平台在几分钟内就发出了告警。系统完整地还原了整个执行链------从脚本解释器开始,经过内核加密子系统,到特权提升二进制文件,基于机队范围的行为模式将其标记为恶意行为。
这个过程没有更新签名,没有修改规则,也没有人工干预。这一确认至关重要------在编写任何针对这个漏洞的专项规则之前,团队就已经有了检测覆盖能力。
工作线二:溯源狩猎------证明"未被利用"
Cloudflare 对严重漏洞的处理原则是:在能够证明无影响之前,假设已被攻击。
团队在集中日志基础设施中搜索漏洞利用的特征痕迹,覆盖漏洞公开披露前 48 小时的数据。随后拉取受影响系统的访问日志,重建所有连接、时间和执行命令的完整记录。同时核验系统二进制文件未被篡改,通过密码学哈希与已知良好的软件包清单比对,检查持久化机制,审计网络连接。最终,一切均无异常。
工作线三:寻找无需重启的缓解方案
内核补丁的正常更新流程需要数周,而漏洞已经公开。团队开始同步寻找不依赖重启的运行时缓解方案。
缓解方案选型:为什么最简单的那个不能用?
方案一:卸载 algif_aead 模块(被否)
漏洞在 algif_aead 模块里,最直接的做法是卸载它:
bash
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
这正是"Copy Fail"漏洞研究人员在披露文章中推荐的缓解方式。
但 Cloudflare 测试后发现,卸载模块会影响依赖内核加密 API 的软件。这意味着需要找到一种更精准的缓解方式。第一次尝试将缓解推送到预生产数据中心时,暴露了一个依赖冲突,变更被回滚------好在这发生在内部测试环境,没有影响任何生产系统。
方案二:bpf-lsm 精准封堵(采用)
Cloudflare 此前已经为类似场景开发并部署过 bpf-lsm 这个工具。它不卸载模块,而是通过 BPF Linux Security Module 程序,在 socket_bind LSM 钩子上拒绝未授权进程的绑定请求,让合法用户继续正常使用,同时对其他所有人关闭前门。
程序逻辑很清晰:
每次 socket_bind 调用发生时:若套接字族不是 AF_ALG,直接放行;若是 AF_ALG,则检查调用方二进制文件的路径是否在白名单上;若在白名单,放行,否则拒绝。
部署 bpf-lsm:先看清楚,再动手
这里有一个工程细节值得单独讲。
在启用 bpf-lsm 的拦截之前,团队需要先确认整个机队里到底有哪些程序在合法使用 AF_ALG 接口------如果白名单不完整,防护会误伤正常服务。
团队使用 prometheus-ebpf-exporter 钩住 socket() 系统调用,按二进制文件追踪整个机队的 AF_ALG 使用情况。这不需要任何内核变更,在数小时内就从数十万台服务器收集到了汇总数据。结果确认,只有一个已知的内部服务是唯一合法的 AF_ALG 使用者。
有了这个可见性数据,bpf-lsm 的部署分两步走:
第一步,先建立可见性 :推送 ebpf-exporter 配置,在指标层确认已知服务确实是唯一创建 AF_ALG 套接字的进程。第二步,再执行拦截:将 bpf-lsm 程序推送到独立的执行闸门后面。
这个"先看清楚、再动手"的顺序,避免了因为白名单不完整而导致的误操作。
完整事件时间线
| 时间(UTC) | 事件 |
|---|---|
| 4月29日 16:00 | Copy Fail 公开披露 |
| 4月29日 ~21:00 | 安全与工程团队开始评估暴露情况和缓解选项 |
| 4月29日 22:52 | 确认现有行为检测覆盖 Copy Fail 的利用模式,内部验证几分钟内触发告警 |
| 4月29日 23:01 | 行为检测生成高严重性告警,确认对该技术的检测能力 |
| 4月29日(晚) | 第一次缓解尝试推送至预生产数据中心,发现依赖冲突后回滚,未影响生产系统 |
| 4月29日(连夜) | 工程师起草 bpf-lsm 缓解程序 |
| 4月30日 03:14 | 正式宣布安全事件,驱动跨团队协作;安全团队完成全机队历史数据威胁狩猎,确认无恶意活动 |
| 4月30日(上午) | bpf-lsm 缓解程序测试完成,具备生产就绪条件 |
| 4月30日 14:25 | 宣布工程事件,协调缓解程序和内核补丁的推出 |
| 4月30日 ~17:00 | 决策确定:通过重启自动化推送已修复内核;期间依靠 bpf-lsm 提供保护 |
| 4月30日(下午) | eBPF 可见性管道(追踪 AF_ALG 套接字使用情况)部署至全机队 |
| 4月30日(晚) | bpf-lsm 缓解程序完成全机队部署;在此前存在漏洞的测试节点上端到端验证,确认漏洞不再可利用 |
| 5月4日(上午) | 重启自动化以正常速度恢复运行,携带修复后的内核 |
| 5月4日起 | 已完成早期重启的服务器手动重启以获取补丁;其余服务器按正常重启自动化流程更新 |
事后复盘:三个改进方向
Cloudflare 在事后识别出三个改进方向:更好的内核 API 依赖关系可见性------持续梳理生产服务对内核子系统的使用情况,使后续缓解能够更快做出精准决策而不影响服务;更好的 bpf-lsm 运行时缓解能力------包括更快的部署流程、更完善的操作手册、更好的日志和可见性;以及减少 Linux 内核的攻击面------审计内核配置,主动识别并从构建中移除未使用的模块或功能。
总结
这篇博客表面是一次安全事件的事后复盘,实际上展示了几件更深层的东西。
漏洞本身的技术启示:一个 2017 年为了性能做的内核优化,9 年后变成了一个允许任意用户以 root 身份执行代码的提权路径。漏洞的每一层------AF_ALG 接口、页缓存的全局共享特性、scatterlist 的越界写入------单独看都不是问题,组合在一起就是一条完整的攻击链。这再次说明,安全问题往往不在于单点的错误,而在于机制之间意料之外的交互。
应急响应的方法论:Cloudflare 对关键漏洞的处理原则是"在证明无影响之前假设已被攻击",从这个假设出发系统性地排除,而不是乐观地假设没事。这种"默认担忧"的工作模式,是大规模基础设施安全运营的基本态度。
工具优先于规则:bpf-lsm 在漏洞披露之前就已经存在并且可用,行为检测在漏洞披露之前就已经能够识别利用模式。负责任的披露有效,内核可见性工具的投入在这样的时刻得到了回报,bpf-lsm 仍然是运行时内核缓解最有价值的工具之一。
对于在生产环境中大规模运营 Linux 系统的团队,这篇事件记录是一份不可多得的参考材料。
参考来源:Cloudflare Blog --- "How Cloudflare responded to the 'Copy Fail' Linux vulnerability"