一个内核提权漏洞最容易被误读的时刻,不是刚披露,而是 exploit 被人跑通之后。

CVE-2026-31431,外号 CopyFail,最近被 Andrea Veri 放进 rootless Podman 里实测。结果很微妙:漏洞确实触发了,容器里也确实拿到了 root;但它没有直接穿透到宿主机 root。

这句话得拆开看。

它不是“容器安全神话”。它说明的是另一件更现实的事:rootless 容器没挡住漏洞,挡住了最坏后果的一部分。

发生了什么:CopyFail 打的是页缓存,不是普通文件写入

CopyFail 的危险点,不在于它能不能写某个文件。

它绕过的是很多人默认信任的文件权限边界,改的是页缓存。公开 exploit 的路径大致是:

环节动作结果
AF_ALG绑定 authencesn(hmac(sha256),cbc(aes))进入内核加密相关路径
sendmsg / splice分块送入恶意 ELF 内容污染 /usr/bin/su 的页缓存
执行 su进程读到被污染的缓存页跑起恶意 payload

payload 也不花哨。解开后是一个很小的 64 位 ELF,做两件事:setuid(0),然后 execve('/bin/sh')

复现实验环境是 Fedora 43,kernel 6.17.1-300.fc43.x86_64。这是修复前版本。补丁进入 stable 6.19.x,从 6.19.12 开始。

所以这不是容器里自娱自乐的小洞。它打的是 Linux 内核路径,绕过的是文件权限和执行边界之间那层大家平时不太看的灰区。

为什么重要:rootless 没赢,User Namespace 赢了一次

最容易说错的一句话是:rootless Podman 阻止了 CopyFail。

没有。

复现显示,exploit 在 rootless Podman 容器里成功污染了 /usr/bin/su 的页缓存,也成功执行了 payload。容器内提示符变成 root,setuid(0) 在无 strace 干扰时返回成功。

真正把事情拦在宿主机 root 之外的,是 User Namespace 的 UID 映射。

容器内 UID 0,不等于宿主机 UID 0。rootless Podman 会把容器里的 root 映射到宿主机上的普通用户 UID。内核允许它在命名空间里成为 root,但这个 root 落到宿主机上,仍是非特权用户。

这就是这次复现最有价值的地方。它把“容器 root”这四个字从口号里拽回了现实:

  • 容器内 root 仍然危险;
  • 宿主机 root 没有自动失守;
  • 是否穿透,取决于 namespace、capability、挂载、seccomp、SELinux 这些边界有没有一起站住。

安全边界从来不是一堵墙。它更像一组闸门。少关一道,水就会找缝。

还有个细节也值得记住:用 strace 观察 SUID 程序时,会碰到 ptrace 和 secureexec 机制,现场会被工具改变。作者最后改用 bpftrace 从内核 tracepoint 侧看 setuid 行为,才拆清楚“strace 下失败、原生运行成功”的差异。

安全实验里,观测者也会制造噪音。这不是玄学,是内核机制。

谁受影响:发行版要补锅,CI runner 更该紧张

旧问题没有消失:内核漏洞修了,发行版还得把补丁送到用户机器上。

这类漏洞最讨厌的地方就在这里。上游补丁合了,不等于你的服务器安全了。stable 版本线、发行版内核、云厂商镜像、企业内部基线,各自有自己的节奏。最后挨打的,往往是下游运维和平台团队。

这次 rootless Podman 复现,又把影响对象缩得更准了一点。

普通桌面用户当然也该升级,但最该认真看的,是跑陌生代码的环境:

  • CI/CD runner;
  • 开源项目构建机;
  • 内部多租户构建平台;
  • 自动化测试沙箱;
  • 允许开发者提交脚本和依赖的执行环境。

这些地方天生脏。

MR、第三方脚本、构建依赖、测试工具链,任何一个都可能把宿主机变成靶场。你无法假设每个 job 都干净,只能假设总有一个会出事。

rootless 的价值就在这里。它不承诺没有火,但尽量不让火烧穿机房。

这也是我对这次事件判断比之前更明确的地方:CopyFail 不只是“内核又一个本地提权漏洞”。它是一次现实压力测试,测的是平台有没有把最坏假设当成默认配置。

我的看法:问题不在漏洞稀奇,在大家总把边界当装饰

CopyFail 技术上有意思,但行业教训并不新。

Linux 本地提权漏洞从来不是罕见动物。真正反复出问题的,是人们对“边界”的想象太乐观。

文件权限看起来硬,页缓存绕过去了。

容器 root 听起来吓人,User Namespace 把它压回普通用户了。

rootless 听起来安心,危险挂载、过宽 capability、松掉 seccomp、关掉 SELinux,又能把安心打回原形。

这就是系统安全最烦人的地方:它不按产品宣传页结算,只按最薄的一层结算。

“天下熙熙,皆为利来。”这句老话放在 CI runner 上一点都不违和。大家要速度,要自动化,要低成本,要更多人能提交代码、跑测试、出包。能力一旦下放,治理就必须前置。

铁路、电力、云平台都走过这条路。不完全一样,但结构相似:高风险能力被规模化分发之后,事故不再只来自坏人,也来自默认配置、赶工流程和没人愿意付钱的安全边界。

rootless Podman 这次算做对了一件事。

但别把它供起来。

它挡住的是“容器内 root 直接等于宿主机 root”这条路,不是挡住了所有后果。容器内 root 仍可能控制容器内文件、进程、网络和挂载资源。只要你把宿主机目录挂进去,把敏感 socket 暴露进去,把 capability 放宽,墙就会从设计变成摆设。

所以接下来该看的不是一句“是否受影响”,而是几件很具体的事:

  • 内核是否已经升到包含修复的版本,例如 stable 6.19.12 之后的相关补丁线;
  • 发行版是否已回移补丁,而不是只在上游仓库里好看;
  • CI runner 是否默认 rootless;
  • User Namespace、seccomp、SELinux/AppArmor、capability 是否一起启用;
  • 构建任务有没有不必要的宿主机目录、Docker socket 或特权模式挂载。

这里没有优雅答案,只有脏活清单。

但安全很多时候就是脏活。模型再漂亮,系统边界不硬,最后都是裸奔。容器安全也是这个道理。

CopyFail 至少让一件事变得更清楚:补丁是第一步,隔离是第二步,默认不信任才是平台活下去的习惯。