🌐 **图书馆的隐秘裂隙:当缓存成为战场**
想象一下,你正置身于一座宏伟的古老图书馆。库房深处,珍贵的原稿被层层铁锁守护,唯有管理员——也就是root权限——才能触碰。普通读者如你我,只能借阅复印件,这些复印件整齐摆在阅览室,供人翻阅却严禁涂改。这便是Linux文件系统的日常:硬盘上的真实文件如原稿,page cache则是阅览室的复印件。系统为求速度,将频繁读取的文件内容暂存于内存,程序读到的往往是这份“影子”而非硬盘本身。
可这一次,一位不速之客——CVE-2026-31431,代号Copy Fail——悄然溜进阅览室。它并未撬开库房铁锁,却在复印件上留下了淡淡墨痕。普通用户无写权限,却成功篡改了内存中的文件镜像,进而借由任意代码执行,悄然登上root宝座。这场攻击,如同幽灵在纸页间低语,改写的不是原稿,而是那份本该不可侵犯的“复印件”。
> **什么是page cache?**
> Page cache是Linux内核为提升I/O性能而设的内存缓存机制。当文件被读取时,内核将其内容按页(通常4KB)载入物理内存。下次访问时,直接从内存取用,避免缓慢的磁盘操作。这极大加速了系统,却也制造了“内存版文件”与“磁盘版文件”的微妙分离。攻击者正是利用这一分离,在不触碰磁盘的前提下,污染了内存镜像。
🌟 **Setuid的信任幻影:普通用户如何短暂化身超级管理员**
在Linux权限世界中,setuid宛如一枚古老的魔法徽章。某些程序文件被标记setuid位后,普通用户运行它时,进程会临时继承文件所有者(通常是root)的权限。最经典的例子莫过于passwd命令:用户修改密码需写入/etc/shadow,此文件唯有root可写。系统于是将passwd程序设为root所有并开启setuid,信任它不会作恶。
这信任本是善意,却成了Copy Fail的破绽。攻击者无需修改磁盘上的setuid程序,只需污染其page cache中的镜像。待程序执行,内核仍视其为“可信原版”,实则已是被篡改的幽灵版本。想象一位守卫,手持看似完好的钥匙,却不知钥匙齿已被悄然锉动——门依旧打开,守卫却已沦为内鬼。
这种机制让普通用户得以“借力”,却也暴露了信任模型的脆弱:系统假设setuid程序文件不可被非特权用户修改,可内存缓存却成了绕过这一假设的捷径。
🔄 **Splice的零拷贝魔术:直接递上原稿而非复印**
为何系统会误将“只读复印件”当成可涂写的草稿?答案藏在splice()这个高性能内核机制中。
传统read()流程如层层转运:磁盘→page cache→拷贝至用户空间buffer。每次都要复制数据,代价不菲。Splice()则追求极致高效——它不复制数据,而是将page cache中的struct page直接挂到pipe_buffer上。pipe持有的是对内存页的引用,而非副本。随后,pipe可无缝传递给其他内核模块,如网络或加密接口。
> **零拷贝的直观比喻**
> 就像图书馆管理员不再为每位读者复印书籍,而是直接把珍贵原稿(page cache)推到阅览桌前,读者只需看,无需额外复印。速度飞快,却也意味着“原稿”暴露在更多操作路径下。若后续环节不严格遵守“只读”规则,后果不堪设想。
Splice()本意是减少内存拷贝、提升吞吐,却在特定组合下,打开了通往越权写入的暗门。
🔐 **AF_ALG与AEAD:内核密码学的用户接口**
Linux内核内置强大密码学框架,为避免用户态反复实现,提供了AF_ALG套接字族。用户程序可像使用socket一般,将数据提交给内核执行哈希、加密、解密或认证。Copy Fail聚焦于其中的AEAD——Authenticated Encryption with Associated Data,带关联数据的认证加密。
AEAD将数据分为两部分:待加密的正文,以及无需加密但需参与认证的关联数据(AD)。这在网络协议中极为常见,确保头部信息未被篡改。用户通过AF_ALG提交数据,内核框架接手处理,整个过程看似安全壁垒森严。
🌊 **Algif_aead的In-Place优化:致命的“共用内存”假设**
2017年的内核改动中,algif_aead引入了in-place操作:允许输入缓冲区与输出缓冲区共用同一块内存,省去一次拷贝。这极大提升性能,却建立在一个关键假设上——提供的buffer必须是可写的。
普通用户空间buffer自然满足这一假设。可当buffer来自splice()的pipe_buffer时,它指向的却是page cache的只读页面。内核未能严格区分二者,于是悲剧悄然发生:本该只读的缓存页,被当作可写区域对待。
这如同管理员将珍贵原稿直接交给读者涂写,却坚信“这是可擦除的草稿纸”。性能优化与安全边界在此刻发生了危险的碰撞。
🛠️ **Authencesn的四字节幽灵:越权写入的真正肇事者**
真正触发写入的,是AEAD具体实现模板authencesn。Authenc代表认证+加密组合,ESN则是扩展序列号,常用于IPsec ESP等协议。
在处理过程中,authencesn需构造认证输入,为底层哈希算法准备序列号字段——整整4字节,位置约在关联数据长度加上加密数据长度之后。正常情况下,此写入应落在可写输出buffer或临时区。可在in-place模式下,输入即输出,写入直接落到了pipe_buffer引用的page cache页上。
更隐蔽的是,这一写入发生在认证tag校验之前。即使最终解密失败,内核返回EBADMSG错误,page cache的污染已然完成。攻击者无需构造合法密文,失败的请求本身就足以改写4字节。
> **四字节为何致命?**
> 二进制程序中,区区4字节可改写跳转指令、条件判断或关键数据结构。攻击者通过反复发起请求、精准控制偏移,便能对setuid程序的内存镜像进行“微创手术”,植入后门而不留磁盘痕迹。
📜 **完整攻击链:从阅读到篡改,再到登顶**
攻击流程如精心编排的戏剧:
1. 攻击者挑选可读但不可写的目标——典型如setuid root程序。
2. 通过splice()将其内容接入pipe,pipe_buffer持有page cache页面的引用。
3. 将pipe数据提交给AF_ALG的AEAD接口。
4. Algif_aead以in-place方式处理,假设buffer可写。
5. Authencesn在计算位置写入4字节序列号,直接污染page cache。
6. 重复操作,多处精准修改内存镜像。
7. 执行setuid程序,系统加载已被篡改的page cache版本,攻击者获得root权限。
整个过程Living off the Land,不改磁盘、不变权限、不触发文件监控,仅在内存中完成“幽灵篡改”。这正是其高明与危险之处。

想象一位普通图书馆读者,借阅一本《系统守护手册》。他无法在原书上写字,却通过巧妙的手法,让阅览室里的复印件上出现了自己的批注。待管理员按这本“被改”的复印件执行指令时,权限的大门悄然洞开。
**防护之道:修补信任的裂痕**
最直接有效的防护,便是更新内核,移除algif_aead的in-place逻辑,强制输入输出分离,确保写入不会落在page cache上。若暂无法升级,可禁用AF_ALG相关模块,限制该接口调用。根本之策,仍是及时打上官方补丁,让这只内存幽灵永无藏身之处。
在这场内存与权限的博弈中,Copy Fail提醒我们:性能优化如双刃剑,信任模型需时刻审视。Linux的强大源于其开放与高效,而安全则需每一位开发者与用户共同守护。
---
**参考文献**
1. Linux内核文档:Page Cache机制概述。
2. Linux man page:setuid权限模型详解。
3. 内核源码分析:splice()零拷贝实现。
4. AF_ALG与AEAD框架技术白皮书。
5. CVE-2026-31431官方补丁说明与分析报告。
登录后可参与表态
讨论回复
0 条回复还没有人回复,快来发表你的看法吧!