技术解读
Dirty Frag:把页缓存变成提权跳板的又一次尝试
项目背景与痛点
Linux 内核的页缓存(page cache)一直是本地提权研究的富矿。从 2016 年的 Dirty COW(CVE-2016-5195)到 2022 年的 Dirty Pipe(CVE-2022-0847),再到 2024 年的 Dirty Pagetable,攻击者的思路一脉相承:找到一个能让用户态"合法地"拿到某个内核页引用、又能在语义上把它当普通内存写的地方。一旦打通,只读挂载的 /etc/passwd、setuid 二进制文件、容器镜像里的只读层,都会在一瞬间失去保护。
V4bel/dirtyfrag 延续了这条命名谱系。从仓库名与作者的公开研究脉络看,它聚焦的是 socket fragment(skb frags)与 splice 零拷贝路径之间的页引用语义错配:网络栈在收发包时大量使用 scatter-gather,skb 的 frags 数组里存的是裸 struct page * 而非数据副本;而 splice() 又允许把 pipe_buffer 里的页引用直接"搬运"进 socket。两套机制各自都做过安全校验,叠在一起却容易在引用计数、标志位清零、页生命周期上出现裂缝——Dirty Frag 就是在这个缝隙里做文章。
它解决的不是"某个业务痛点",而是安全研究领域的一个真问题:当内核越来越多的关键路径为了性能而共享页引用时,传统的 copy-based 安全检查还够不够用?
核心技术揭秘
从利用链条来看,Dirty Frag 的关键在于"取得页引用的可写别名",而非暴力对抗权限检查。整条数据流大致可以拆成四个阶段:页缓存页定位 → 引用搬入 pipe/skb → 引用计数失衡 → 页重用获得可写映射。
第一阶段是"借道"。splice() 的语义是零拷贝,pipe_buffer 里的 page 与目标文件的页缓存页是同一个对象。Dirty Pipe 的根因正是 PIPE_BUF_FLAG_CAN_MERGE 未在新分配的 pipe_buffer 上清零,导致后续 write() 直接往只读页的偏移处合并写入。Dirty Frag 显然吸收了这个教训,它更依赖 skb fragment 对 page 的裸引用:sendfile() / splice() 到 socket 时,内核把页挂进 frags 而不复制内容,此时若页的引用计数或 skb 的释放路径存在偏差,就可能出现"内核仍认为页被持有、实际已可回收"的窗口。
第二阶段是"错配"。页缓存页通常由 inode 的 address_space 管理,回收受 LRU 与引用计数约束。一旦引用计数被 fragment 路径错误地增减,这个页就可能被页分配器回收,转而分配给用户态匿名内存或页表页——这正是 Dirty Pagetable 的思路:与其改页内容,不如让页换个身份。用户态拿到同一物理页的可写别名后,再回头通过原文件的 mmap 观察并验证写入结果,形成"只读文件被就地修改"的效果。
实现上,这类原型通常需要处理几个硬骨头:精确控制 pipe 的填充与释放顺序以命中竞态窗口;构造能让 skb frag 长时间持有页引用的 socket 状态(如设置较小的发送缓冲区、使用 MSG_MORE 挂起发送);以及在目标内核版本上适配 pipe_buffer 结构与 skb_shared_info 的字段偏移。
功能亮点与差异
与 Dirty Pipe 相比,Dirty Frag 的差异点在于不依赖单一漏洞点,而是把"页引用生命周期"当作攻击面——这意味着即使 PIPE_BUF_FLAG_CAN_MERGE 被修掉,只要 fragment 路径的引用语义仍有瑕疵,利用链依然成立。与 Dirty Pagetable 相比,它更轻:不需要构造复杂的页表自引用与 TLB 冲刷,落点更靠近"文件篡改"这一类高价值目标。
另一个亮点是对跨场景可用性的追求。容器共享宿主机内核页缓存,只读挂载、overlayfs 的 lower 层在页缓存层面并不只读,这让该手法在容器逃逸与镜像完整性破坏上具备现实威胁。此外,仓库本身更像一份"可复现的研究载体":结构清晰、依赖少,便于安全团队拿来做检测规则与缓解措施的验证基线。
应用场景与落地建议
对防守方而言,这类项目的价值大于风险。建议从三个层面落地:
- 内核侧:跟进相关 CVE 与上游补丁,重点关注
splice/sendfile到 socket 的页引用路径、pipe_buffer 初始化路径的改动;开启CONFIG_DEBUG_PAGEALLOC、KASAN 与slub_debug做灰度验证。 - 系统侧:对关键只读文件启用 IMA/EVM 完整性度量,用 fs-verity 保护不可变镜像;限制非特权用户的
splice/vmsplice/sendfile组合使用(Seccomp 白名单比 broad deny 更可控)。 - 运营侧:把"只读文件在页缓存中被就地修改"作为检测假设,建立文件哈希与页缓存内容的周期性比对;容器场景下缩短镜像层生命周期、避免长驻只读基线被悄悄改写。
需要提醒的是,生产环境不要指望单一缓解措施。页缓存类漏洞的修复往往反复,务必保持内核版本可快速回滚,并对高风险主机做最小化能力裁剪。
综合评价
优点:技术路线清晰,站在 Dirty COW → Dirty Pipe → Dirty Pagetable 的肩膀上做了合理的机制组合,攻击面选择(fragment 的页引用语义)足够精准;代码组织紧凑,作为研究与检测基线非常实用。
不足:作为 PoC 型项目,它对内核版本与配置高度敏感,通用性依赖大量适配代码;文档与缓解说明偏薄,容易让一线运维误判风险边界;对竞态窗口的依赖也让稳定性打了折扣。
一句话总结:它不是又一个"改只读文件"的技巧,而是一次提醒——当性能优化让页引用在内核里四处流动时,安全边界必须跟着页走,而不是跟着函数走。

评论