Linux 内核 IPv6 协议栈中一个隐藏了四年的账目错误,允许无特权的容器内用户通过精心构造的 UDPv6 报文越界写入 skb_shared_info 结构,实现容器逃逸和 root 提权。
漏洞速览
- CVE 编号:CVE-2026-53362
- CVSS 评分:暂未发布(高危预计 7.8+)
- 影响范围:Linux 内核 v5.19 / v6.0 起的几乎所有主线/发行版内核
- 引入时间:2022 年 7 月(commit 773ba4fe9104)
- 利用方式:本地低权限用户(容器内)→ 堆越界写入 → 容器逃逸 + root 提权
- 修复版本:v7.1.3、v7.2-rc1 及对应稳定分支(2026 年 6 月)
- 触发条件:UDPv6 socket +
MSG_MORE+MSG_SPLICE_PAGES标志组合
技术背景:skb 与 IPv6 分片的故事
要理解这个漏洞,先要了解内核网络栈中两个核心概念。
skb(socket buffer)
skb 是 Linux 网络栈中一切数据包的基本载体。它是一个内核对象,包含:
┌─────────────────────────────┐ │ sk_buff 元数据 │ ← head, data, tail, end 指针 ├─────────────────────────────┤
│ 线性数据区域 (linear area) │ ← skb->head ~ skb->end
├─────────────────────────────┤ │ skb_shared_info │ ← frags[] 页散射数组 │ ┌────────────────────────┐ │ │ │ nr_frags, frags[] │ │ │ │ dataref, destructor │ │ │ └────────────────────────┘ │ → 紧跟在 skb->end 之后 └─────────────────────────────┘
skb_shared_info 紧贴在线性数据区 end 指针之后,是一个极其关键的内核结构。一旦写入超过 end,就等于直接覆盖了 skb_shared_info 的字段。
fraggap 机制
IPv6 分片传递时,如果上一个分片的数据在 8 字节对齐时留有空隙(gap),这个空隙不会丢失——会被累积变量fraggap 跟踪,并在下一个 skb 的线性区头部补齐。换句话说,fraggap 是 IPv6 分片中\"跨包结转\"的碎片大小。
漏洞根因:3 行代码的账目错误
漏洞发生在 net/ipv6/ip6_output.c 的 __ip6_append_data() 函数中。
有问题的代码
当内核走 paged-allocation 分支(即数据量大到需要使用 MSG_MORE + NETIF_F_SG + page 散射机制)时,alloclen 和 pagedlen 的计算为:
alloclen = fragheaderlen + transhdrlen;
pagedlen = datalen - transhdrlen;
问题出在哪里?
datalen 本身已经包含了 fraggap(因为 datalen = length + fraggap)。但在 paged 分支中:
alloclen(线性区大小)没有算上 fraggap,导致线性区比实际需要的少了fraggap字节pagedlen(页散射区大小)多算了 fraggap,因为从datalen中只减了transhdrlen
后果
当内核把 fraggap 字节复制到新 skb 的线性区时:
实际写入: [head ... head + fragheaderlen + fraggap] 分配空间: [head ... head + fragheaderlen] ^^^^^^^^^^^^ 越界写入 skb_shared_info!
简单说:搬了 fraggap 字节的数据,但只给 fraggap 字节少的位置放。 多出来的字节直接写到了 skb->end 之后,覆盖了 skb_shared_info。
为什么之前没被利用?
这个漏洞其实早在 2022 年 7 月(commit 773ba4fe9104)就被引入了。但在此后的三年多时间里,copy 变量在 paged 分支中会算出负值(-fraggap),而这个负触发早期的 -EINVAL 检查直接返回错误,阻止了数据的实际写入。
转折点出现在 commit ce650a166335(\"udp6: Fix __ip6_append_data()\'s handling of MSG_SPLICE_PAGES\")。这个补丁允许 MSG_SPLICE_PAGES 在 copy < 0 时继续执行,使得本应被拦下的路径变成了可触发的漏洞。
触发路径:攻击者怎么做?
攻击者只需要在容器内具备基本的网络能力(CAP_NET_RAW 或 net.ipv6.ping_group_range 配置允许),无需 root 权限:
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
// 关键组合:MSG_MORE + MSG_SPLICE_PAGES
// 构造多个 sendmsg 调用触发分片和 fraggap
关键触发条件:
- ✅ UDPv6 socket(TCPv6 走不同路径)
- ✅ MSG_MORE 标志——告诉内核还有更多数据要来,暂不发送
- ✅ MSG_SPLICE_PAGES 标志——允许内核使用 page 散射而非线性复制
- ✅ 分片触发——数据量超过 MTU,产生多个 skb 和 fraggap
当这些条件满足时,__ip6_append_data() 在 paged 分支上计算出错误的 alloclen,memcpy 写入 skb->end 之后的 skb_shared_info。
为什么是容器逃逸?
skb_shared_info 结构包含的关键字段:
struct skb_shared_info {
__u8 __unused;
__u8 meta_len;
__u8 nr_frags; // ← 可以被覆盖!
__u16 dataref;
struct skb_shared_hwtstamps hwtstamps;
unsigned short gso_type;
u32 gso_size;
struct page *frags[MAX_SKB_FRAGS]; // ← 页指针数组!
};
越界写入 fraggap 字节(通常 1-7 字节)覆盖的是 nr_frags、dataref 等字段的上半部分。精心构造的 payload 可以:
- 篡改
nr_frags让内核解析出非法数量的页片段 - 利用页指针数组 实现任意物理地址读/写
- 绕过内核命名空间隔离 访问宿主机内核的 slab 分配器
- 完成容器逃逸 获得宿主机 root shell
这本质上是一个典型的堆越界写入 → 结构覆写 → 提权的漏洞链路。
修复补丁分析
修复极其简洁——只改了 3 行代码:
// paged 分支
-alloclen = fragheaderlen + transhdrlen;
-pagedlen = datalen - transhdrlen;
+alloclen = fragheaderlen + transhdrlen + fraggap;
+pagedlen = datalen - transhdrlen - fraggap;
加上 fraggap: alloclen 正确计入 fraggap 字节,线性区长度正确。
减去 fraggap: pagedlen 相应减少,避免重复计账。
同时移除了 copy < 0 时的 MSG_SPLICE_PAGES 例外:
-if (copy < 0 && !(flags & MSG_SPLICE_PAGES)) {
+if (copy < 0) {
err = -EINVAL;
现在 paged 分支中的 copy 不会再变成负数,MSG_SPLICE_PAGES 也不再需要特殊豁免。
受影响版本与修复
| 分支 | 受影响起始 | 修复版本 | 修复 commit |
|---|---|---|---|
| mainline | v6.0+ | v7.2-rc1 | 736b380e28d0 |
| 7.1.y | 7.1 | 7.1.3 | 14200d435af9 |
| 6.12.y | 6.12+ | 待确认 | 46f201f8b4c3 |
| 6.6.y | 6.6+ | 待确认 | 6374fb9edf72 |
| 6.1.y | 6.1+ | 待确认 | 65fb14cbebb0 |
受影响的大部分是 2022 年 7 月之后发布的内核,包括 Ubuntu 22.04+、Debian 12+、Fedora 36+、RHEL 9.x 及各种容器运行时(Docker、Kubernetes Node 等)的底层内核。如果容器运行时使用了与宿主机共享内核的配置(默认情况),容器内普通进程即可触发此漏洞。
缓和措施
如果暂时无法升级内核:
- 限制容器内
CAP_NET_RAW——这是最有效的权限收缩,Docker / Kubernetes 默认授予,但非必要应移除 - 禁用 IPv6——
sysctl -w net.ipv6.conf.all.disable_ipv6=1(仅限不需要 IPv6 的环境) - 使用用户命名空间映射——
user.max_user_namespaces=0或配置userns隔离 - 启用内核防护——
slub_debug=UFPZ、CONFIG_SLAB_FREELIST_RANDOM、CONFIG_RANDOMIZE_KSTACK_OFFSET等可以增加利用难度
写在最后
CVE-2026-53362 是一个典型的\"4 行代码修了 4 年的 bug\"故事。2022 年一个小小的账目错误,遇到 2025 年一个放宽 MSG_SPLICE_PAGES 检查的 commit,最终在 2026 年变成了一个高危容器逃逸漏洞。再次印证了一个道理:
内核代码中\"看似永远不会被执行到的路径\",总有一天会被人走出来。
三个关键教训:
- 数值计算的边界条件必须精确——多 1 字节的偏移量就是安全与沦陷的差别
- 放宽安全检查时务必审计关联路径——ce650a166335 的本意是修复 MSG_SPLICE_PAGES 的正常使用,却意外放开了漏洞的扳机
- 容器隔离不解决内核漏洞——公用的内核永远是容器的阿喀琉斯之踵
━━━ ━━━ ━━━
参考文献
- CVE-2026-53362 - NVD
- 修复 commit (主线)——Wongi Lee, Jungwoo Lee
- 修复 commit (7.1.y 稳定版)
- 修复 commit (6.12.y 稳定版)
- 修复 commit (6.6.y 稳定版)
- 修复 commit (6.1.y 稳定版)
- 引入漏洞的 commit: ipv6: avoid partial copy for zc (773ba4fe9104)——Pavel Begunkov, 2022-07-12
- 放宽 MSG_SPLICE_PAGES 检查的 commit: udp6: Fix __ip6_append_data()\'s handling of MSG_SPLICE_PAGES (ce650a166335)
━━━ ━━━ ━━━
*本文完。欢迎关注 LeisureLinux,获取更多深度 Linux 内核与安全技术分析。*
CVE-2026-3071 \"Dirty Frag\":Linux 内核权限提升漏洞深度解析
Copy Fail 内核漏洞已经在各大主流发行版 Ubuntu, Debian 修复,请尽快升级!
隐匿的威胁:论 Linux 内核漏洞的"长寿"现象与企业级补丁防御策略
Bad Epoll 深度拆解:一桩跨越三年、两条机器指令引发的内核血案
libssh2 漏洞深度解读:SSH 库的"重症监护室"时刻
当 Rust 开始吃掉整个 Linux 底层:X11、内核、编译器一起锈化