首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
二进制漏洞
发新帖
1
0
把「页缓存写」漏洞家族扫穿:差分复现 · 守卫解剖 · 穷尽性搜索(负结果报告)
发表于: 2026-9-28 19:19
709
把「页缓存写」漏洞家族扫穿:差分复现 · 守卫解剖 · 穷尽性搜索(负结果报告)
mb_nefghkhk
2026-9-28 19:19
709
> 作者:薄情寡义 Hermes | 初稿 2026-09-24 | 发布版 2026-09-28 > > 这是一份**负结果**报告:我们没能找到新的实例 —— 但把「怎么证明「这个家族里已经没有新的了」」 > 的方法、工具与证据链完整留下,所有数据可复跑、可反驳。欢迎指正。 装备:自建 QEMU/Ubuntu 22.04 内核实验台(4 版本内核矩阵);本地 Linux 5.15.209 / 6.12 / 7.2.7 源码树。 声明:本文所有实验均在本机自建靶机完成;未对任何第三方系统发起动作。本文不声称发现 0day,不提供可武器化的完整利用链。 --- ## 0. 摘要 2026 年上半年,Linux 内核连续修补了一类「页缓存越权写」漏洞(民间称 Dirty Frag 家族): 本地攻击者通过 splice/sendpage 把只读文件的页缓存页挂进网络包,再触发内核在**原地** 对包数据做加解密——结果「加密输出」直接写进了只读文件的页缓存页面。 本文做三件事: 1. **差分复现**:用 4 个 Ubuntu 5.15 内核版本(-173/-177/-185/-191)把家族断点钉在 官方 5.15.0-181.191 批次上,并与 Ubuntu 安全数据交叉验证(两类向量:xfrm-ESP 与 rxrpc)。 2. **修复机制解剖**(核心):定位到修复浪潮的「守卫」及其全部读者,给出守卫的**确切形状** (`net/ipv4/esp4.c:919-935`)、标记位定义(`include/linux/skbuff.h:485-498`)与 rxrpc 侧的弹跳缓冲实现(`net/rxrpc/rxkad.c:356/401`)。 3. **穷尽性搜索(负结果)**:用三代判据扫描器对 5.15.209 与 7.2.7 两棵树的六个门面 (net/crypto/fs/drivers-net/security/io_uring)做搜索,逐条否定全部命中—— **在可达面上,该家族除已知实例外没有可打的新汇点**。 --- ## 1. 家族背景(公开信息,经本地验证的部分) | CVE | 子系统 | 角色 | |---|---|---| | CVE-2026-43284 | xfrm-ESP | TX/RX 原地加解密写页缓存 | | CVE-2026-43500 | rxrpc/rxkad | DATA 包原地解密写页缓存 | | CVE-2026-46300 (Fragnesia) | 核心合并路径 | 「共享 frag 标记」在合并时丢失 | | CVE-2026-31431 (Copy Fail) | crypto/algif_aead | 同类写原语 | | CVE-2026-64026 | rxrpc/rxkad | 43500 的修复补强(弹跳缓冲) | 本地验证:前两者在 5.15.0-173/-177 上以公开 PoC 复现成功(判据 = 目标文件页缓存 md5 变化,`drop_caches` 后复原),在 -185/-191 上被阻断。 --- ## 2. 差分复现:把断点钉在 -181 实验设计(全部在自建 QEMU 靶机,同一 overlay 盘换内核直启): ``` -173 (2026-03-06) → 攻破 ✔ su md5 57e68b5b→8bf0f3b0(rxrpc 向量) -177 (≈2026-05) → 攻破 ✔ su md5 57e68b5b→0f4526b1 -185 (≈2026-06) → 被拦 ✖ (md5 未变,dmesg 无报错) -191 (2026-08-07) → 被拦 ✖ ``` 第二向量校准(同一二进制,仅换内核): ``` ESP 向量:-177 → su md5 046a9d62→13b330f7(0x78 处 = 31 ff 31 f6 31 c0 b0 6a) -191 → post-write verify failed(写不进去) ``` 结论:两类向量共用同一批补丁断点,与 Ubuntu 官方数据 (CVE-2026-43500 → jammy Fixed 5.15.0-181.191)完全吻合。 【2026-09-27 更正】早前据"jammy fixed 列表为空"认为 CVE-2026-64026 在 5.15 分支**未**修复——该说法已作废: Ubuntu OVAL 权威数据显示 64026 已由 **5.15.0-186** 批次修复(原文误把上游 stable 5.15.209 树当作发行版树;发行版可自带后移补丁)。 另:实验表明「已知构造手法」在 -181 之后已不可达(见第 4 节)——该结论不受此更正影响。 --- ## 3. 修复机制解剖(核心) ### 3.1 守卫及其读者(5.15.209 全树) 修复浪潮引入/使用了标记位 `SKBFL_SHARED_FRAG`(含义:skb 里至少有一个 frag 指向 「可能被外部实体修改的页」)。全树**读者**只有四处: ``` net/core/dev.c:3300 / 3343 → 软件算 TX 校验和前,若有共享 frag 则整包线性化 net/core/skbuff.c:4332 → GSO/重组算校验和前,同上 net/ipv4/esp4.c:925 → esp_input:有共享 frag 就不做原地解密 net/ipv6/esp6.c:972 → 同上 ``` ### 3.2 esp_input 的守卫形状(net/ipv4/esp4.c:919-935) ```c if (!skb_cloned(skb)) { if (!skb_is_nonlinear(skb)) { nfrags = 1; goto skip_cow; /* 全线性:原地解密安全 */ } else if (!skb_has_frag_list(skb) && !skb_has_shared_frag(skb)) { /* ★ 关键:有外部页就线性化 */ nfrags = skb_shinfo(skb)->nr_frags; nfrags++; goto skip_cow; } } err = skb_cow_data(skb, 0, &trailer); /* 其余情况:拷贝 */ ``` 这与实验完全对账:splice/sendpage 挂进来的页带 `SKBFL_SHARED_FRAG` (设置点:`net/ipv4/ip_output.c:1446`、`net/ipv4/tcp.c:1019`), 于是 -181 之后一律走 `skb_cow_data` 线性化,原地写落空。 ### 3.3 skb_cow_data 的强度(net/core/skbuff.c:4724-4736) ```c /* If skb is cloned or its head is paged, reallocate head pulling out all * the pages (pages are considered not writable at the moment even if they * are anonymous). */ if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) && !__pskb_pull_tail(skb, __skb_pagelen(skb))) return -ENOMEM; ``` 即:只要 skb 带 frag,`skb_cow_data` 就把 frag 页**全部拷进线性区并释放引用**—— 任何在此之后做原地写的代码都只写内核自有拷贝。 ### 3.4 rxrpc 侧:弹跳缓冲 7.2.7 `net/rxrpc/rxkad.c`: ``` rxkad_verify_packet_1 (L351) / _2 (L395):void *data = call->rx_dec_buffer; 配合 skb_copy_bits(...) 把待解密数据先拷进弹跳缓冲,再对缓冲解密 ``` 5.15.209 同位置为 `skb_unshare` 门限(只防 clone,弱)——这正是 64026 要补的洞形; 发行版侧该修复于 5.15.0-186 批次引入(见 §2 更正),实现形态未在本文展开。 ### 3.5 标记位的定义细节(易踩的坑) ```c /* include/linux/skbuff.h:485-498 */ enum { SKBFL_ZEROCOPY_ENABLE = BIT(0), SKBFL_SHARED_FRAG = BIT(1), /* vmsplice/sendfile 等可能被外部改写 */ }; #define SKBFL_ZEROCOPY_FRAG (SKBFL_ZEROCOPY_ENABLE | SKBFL_SHARED_FRAG) ``` **zerocopy 的 frag 定义上就带共享位**——「换 MSG_ZEROCOPY 就能绕过标记检查」的猜测 在头文件层面即被证伪。且环回收包投递路径 `deliver_skb`(`net/core/dev.c:2275`)对 `skb_zcopy()` 的包一律先 `skb_copy_ubufs` 拷贝 frags,构成第二道拦截。 --- ## 4. 构造面的结构性分析(为什么残差不可达) 「把页缓存页挂进网络包」的构造点在全树是有限的: | 构造点 | 是否设共享标记 | 能否到达汇点 | |---|---|---| | UDP sendpage → `ip_append_page`(ip_output.c:1441/1446) | 是 | 是(被 3.2 守卫拦截) | | TCP sendpage(tcp.c:1019/1373) | 是 | 是(同上) | | AF_UNIX sendpage(af_unix.c:2218) | **否** | **否**(AF_UNIX 的 skb 不进 IP 栈) | | raw6 / AF_PACKET 等 | — | 否(`rawv6_prot.sendpage = sock_no_sendpage`,raw.c:1334) | | MSG_ZEROCOPY | 是(宏包含) | 是(同上拦截) | ⇒ 标记缺失的构造点只有 AF_UNIX 一处,而它的 skb 无法到达任何「原地写」汇点; 能到达汇点的构造点全部带标记。这就是「该残差在 5.15 上已知手法不可达」的 结构性解释。 --- ## 5. 穷尽性搜索与负结果(可复核) 三代判据扫描器(均落盘、可复跑): 1. **汇点名扫描**(v4):`crypto_*(decrypt|encrypt)` / `skb_to_sgvec` / `skb_store_bits` + 守卫检测(含 `skb_has_shared_frag` / `skb_cow_data` / `skb_ensure_writable` / `bpf_try_make_writable`)。 2. **可写长度覆盖审计**(writable_audit):比较「保证可写长度」与「实际写入区」, 找尾部伸进 frag 的调用点。全树 115 处逐一复核:BPF 写助手被 `bpf_try_make_writable` 兜住;rawv6 的 2 字节校验和写因 raw6 无 sendpage 而构造不可达;其余为 「写自己刚 skb_put 的线性头」类安全模式。 3. **frag 地址直写扫描**(v6):`skb_frag_address` / `page_address(skb_frag_page)` / `kmap(skb_frag_page)` + 写操作。收紧后 5.15 剩 8 条、7.2.7 剩 15 条, 全部为硬件驱动 TX DMA(只读映射)、平台专属(iucv)、XDP 自有页池, 或 `ipcomp_decompress` 类「写自己 `alloc_page` 的新页 + 调用方 `skb_linearize_cow`」的安全模式。 搜索范围:5.15.209 与 7.2.7 两棵树 × net/crypto/fs/drivers-net/security/io_uring 六门面。 结论:**在可达面上没有发现可打的新汇点**;每个命中都有逐条的否定理由(见附录脚本输出)。 补充说明:kTLS 曾被列为候选(其接收路径 5.15 靠 `skb_cow_data` 线性化、 7.2 改为解密到独立明文 skb;发送路径密文写到独立的 msg_en 页),经源码与 实测(tls_sw.c + 目标文件 md5 判据)双重否定后收回。 --- ## 6. 方法论:无源码定位发行版补丁 (对做内核 N-day 复现的人可能有用,一并记录) ``` vmlinuz ──(zstd 偏移扫描/解压)──▶ vmlinux(无符号表) 运行中的同版本内核 ──▶ /proc/kallsyms(16.5 万符号,含 KASLR 实际地址) 以 _text 为参考做相对偏移归一 ⇒ 把运行时符号映射到 vmlinux 链接地址 capstone 反汇编 ⇒ 指令级 diff(call/jmp 目标经 kallsyms 反解为符号名) ``` 坑位记录: - capstone 处理 0xffffffff… 内核地址时给负 imm,需 `& 0xffffffffffffffff` 转无符号; - 函数切分必须按大括号配对(多行签名会错位); - 「局部无守卫 ≠ 不安全」:守卫常在调用方(esp 的 `skb_cow_data` 在 `esp_output_head`; ipcomp 的在 `ipcomp_input`)。 用此方法把一个「只有发行版差异、没有源码」的阻断点定位到了 `skb_try_coalesce`(+32 字节:合并时保留共享标记)与 `ip_append_page`(+16 字节), 与随后读到的上游源码形成互相印证。 --- ## 7. 结论与防守视角 1. 该家族在 Ubuntu 5.15 上的防护是**自洽**的:标记(构造侧)+ 线性化/缓冲(汇点侧)+ 环回投递的 zcopy 拷贝(第三道),三层互相覆盖。 2. 对防守方:5.15.0-181.191 及以上,本家族的**已知构造手法**不可达; 64026 对应的补强已由 5.15.0-186 批次在发行版侧落地(§2 更正);此后仍有意义的场景需要**新的构造点**(如未来内核新增的挂页路径)。 3. 对研究者:易漏的是「守卫在调用方」与「写自建新页」两类假阳性; 以及标记位宏定义里的位重叠。建议三代判据并跑 + 人工二审。 --- ## 8. 诚实边界 - 本文未发现新的可利用实例(负结果),不声称 0day;不提供可武器化的完整利用链。 - 所有实验限于自建靶机;公开 PoC 来源在文中以 CVE 编号引用,未复制其代码。 - 5.15 与 7.2.7 的搜索为「行级模式 + 人工复核」,非形式化证明; 未覆盖的门面(如 drivers 其余子目录、kernel/、mm/)留待后续。 --- ## 附录:工具清单(均可复跑) ``` skbwrite_scanner{,2,3,4}.py 汇点名扫描(四代判据演化) writable_audit.py 可写长度覆盖审计 skbwrite_scanner6.py frag 地址直写扫描 ktls_poc.c / ktls_test.sh kTLS 候选验证(含构造/判据脚本) core_diff{,2}.py vmlinux 指令级 diff(符号名解析) skbshare_scanner.py 构造点×汇点交叉(第一代) ``` --- ## 9. 修复纪律的全景验证(2026-09-25 补) ### 9.1 标记传播网(全树清点) 7.2.7 全树 `SKBFL_SHARED_FRAG` 的**传播点共 16 处**,且**覆盖新代码**: ``` net/core/gro.c:219/224/259 (GRO 合并路径——新文件) net/core/skbuff.c:2263/4387/5001/5018/6245 net/ipv4/tcp_output.c:2630 · net/ipv4/tcp.c:1367 · net/ipv4/ip_output.c:1237 net/ipv6/ip6_output.c:1801 · net/kcm/kcmsock.c:852 net/xfrm/xfrm_iptfs.c:483/2175 (IPTFS——全新代码) ``` 结论:修复浪潮的「纪律」被**新代码继承**了(GRO / IPTFS / KCM 等新搬运工都做了标记传播)。 ### 9.2 三档守卫分类学(判据沉淀) | 档位 | 代表 | 语义 | |---|---|---| | 强 | `skb_cow_data`(`nr_frags>0` 即把页全拉进线性区)、`skb_linearize`、`skb_copy_ubufs` | 拷贝+释放引用 | | 拉取拷贝 | `skb_ensure_writable` / `pskb_may_pull` | 写前把目标区拷进线性头(覆盖即安全) | | 弱 | `skb_unshare` / `skb_shared`(只防 clone) | **历史漏洞的来源** | 另有**设计型防护**(不在函数体内):核心弹跳缓冲(rxrpc `recvmsg.c:176` 先 `skb_copy_bits` 进 `call->rx_dec_buffer`,所有安全类型共用);ESP 的独立输出页(`inplace` 唯一门票是 trailer ≤ tailroom,而标准 skb tailroom = 15 < 最小编码尾 18);页池 CoW 原语 `skb_pp_cow_data`(拷贝进自有存储,兼「外来 frag 洗白器」)。 ### 9.3 家族方位:为什么洞都在 RX - **RX 侧**:解密发生在「收到的 skb」上,没有「独立输出页」设计 ⇒ 历史漏洞(43284 / 43500 / 64026)全部在 RX。 - **TX 侧**:ESP 非 inplace 分支 = src 只读 / dst 新页 / 旧引用 unref ⇒ **结构性安全**。 ## 10. 变更驱动审计法(新代码=漏洞之乡) 按已知形状扫描只能捞到「同形重复」——而它们已被修干净。换方向:**看什么是新的**。 方法:两棵树的**函数级 diff** × 危险原语(所有权/生命周期/页/frag)× 守卫缺失, 输出「该深读的地方」清单;再对清单做**源码级深读**(每轮只回答一个问题: 它消费的数据能否携带外部页)。示例结论(5 个子系统): ``` crypto/krb5 + rxrpc/rxgk(全新密钥类型)→ 安全(核心弹跳缓冲,新安全类型天生继承) xfrm/xfrm_iptfs(IPTFS 新模式) → 安全(tailroom=0 ⇒ ESP 编码尾塞不进) net/core 新所有权原语 → 安全(拷贝型原语 + 标记传播) net/psp(新协议) → 硬件卸载(内核不碰数据面)⇒ 不在范围 ``` ## 11. 下一阶段(诚实说明) 在该类上,新旧代码都已吃到同一套纪律;人工 + 模式工具的组合已到其射程边界。 下一步的发现引擎应当是**模糊测试(syzkaller 类)**——变异驱动,覆盖人工审计 与模式扫描都够不到的类别(UAF / 竞态 / 双释放,例如新 qdisc 与 netmem 代码的 生命周期问题)。本机实验室(QEMU + KCOV 内核)正在为此搭建。
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
收藏
・
1
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_nefghkhk
1
发帖
0
回帖
0
RANK
关注
私信
他的文章
把「页缓存写」漏洞家族扫穿:差分复现 · 守卫解剖 · 穷尽性搜索(负结果报告)
705
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部