文章总结: DirtyFrag是Linux内核本地权限提升漏洞链(CVE-2026-43284和CVE-2026-43500),利用splice()零拷贝机制将只读文件页缓存引用植入网络发送缓冲区,结合IPsecESP或RxRPC模块的原地解密操作实现任意可读文件页缓存写入。漏洞影响2017年以来主流Linux发行版,无需竞争条件且成功率接近100%。文档还探讨了利用authenc模块和AES-CBC密码学知识控制解密结果的新攻击路径。 综合评分: 85 文章分类: 漏洞分析,渗透测试,红队,内网渗透,安全工具
DirtyFrag浅析及一条新的攻击路径
wanglxi wanglxi
看雪学苑
2026年8月5日 17:59 上海
在小说阅读器读本章
去阅读
一、前言
2026 年 5 月 7 日,韩国安全研究员 Hyunwoo Kim(@v4bel)向 oss-security 邮件列表公开披露了一个名为 Dirty Frag 的 Linux 内核本地权限提升漏洞链,并附带了完整的技术分析和 PoC 代码。
#
这个名字精确地概括了漏洞的本质:攻击者通过 splice() 零拷贝机制,将只读文件的 page cache 页面引用植入网络发送缓冲区(skb)的 frag 槽位。当接收端内核在 esp4/esp6(IPsec ESP)或 RxRPC 模块中执行原地解密操作时,本应对密文的解密变成了直接写入只读页缓存的 STORE 原语。
当这两个独立子系统(xfrm-ESP 和 RxRPC)在 splice() 的帮助下与 Page Cache 交汇时,一个无特权的本地用户可以向系统中任意可读文件的页面缓存写入可控数据。
漏洞链由两个 CVE 组成:
-
CVE-2026-43284
涉及 IPsec ESP(esp4/esp6)模块,自 2017 年 1 月引入内核,潜伏长达 9 年
-
CVE-2026-43500
涉及 RxRPC 网络模块,于 2023 年 6 月引入内核
漏洞影响 2017 年以来几乎所有主流 Linux 发行版(CVSS 评分 7.8 High),且无需竞争条件,成功率接近 100%。
ℹ️ 时间线
#
二、漏洞解析
我们从历史发展的角度,来看漏洞的由来,这样可能更通俗易懂。
Dirty Cow Linux高危漏洞Dirtycow整理
https://zhuanlan.zhihu.com/p/25918300
它发现了一个Linux内存管理的漏洞,可以越权修改文件内容, 比如修改 /etc/passwd 文件。
Dirty Pipe CVE-2022-0847 dirty Pipe 漏洞原理深度分析 近年来最”干净”的内核提权漏洞之一
https://bbs.kanxue.com/thread-291662.htm
它发现了一种使用pipe + splice直接修改文件缓存的方法,比如修改 /usr/bin/su 文件。
Copy Fail Copy Fail 深度研究:Linux 页缓存漏洞的根因、利用与检测
https://bbs.kanxue.com/thread-291167.htm
探索CVE-2026-31431的热补丁修复方案
https://bbs.kanxue.com/thread-291542.htm
在Dirty Pipe被修复后,它发现了 pipe + splice + socket + authencesn 也可以修改文件缓存。
Dirty Frag 在Copy Fail被修复后,它发现了 pipe + splice + socket + esp + authencesn 的方式来修改文件缓存。(RxRPC暂未研究,就此略过)
内核支持使用socket来操作authencesn, Copy Fail只需要构造特定网络包调用解密api就可以,相对简单;
而IPsec ESP模块是用来处理网络加密的模块,所以Dirty Frag就需要搭建一个能走到ESP模块esp_input函数的网络环境。
所以相对Copy Fail就是多了一个网络环境的搭建,其他部分大同小异。
Dirty Clone Dissecting and Exploiting Linux LPE Variant: DirtyClone (CVE-2026-43503)
https://research.jfrog.com/post/dissecting-and-exploiting-linux-lpe-variant-dirtyclone-cve-2026-43503/
在Dirty Frag被修复后,它发现 pskb_copy 会丢失关键Flag,从而让补丁失效。
#
三、漏洞详情
1. esp4模块中的漏洞
esp4部分的关键代码如下,主要目标是跳过 skb_cow_data 函数(它会给数据重新分配一段内存)。
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
//.....
if (!skb_cloned(skb)) { // <== 非clone
if (!skb_is_nonlinear(skb)) { // <== [1]线性
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) { // <== [2]无分片
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow;
}
}
err = skb_cow_data(skb, 0, &trailer);
if (err < 0)
goto out;
nfrags = err;
skip_cow:
//.....
err = crypto_aead_decrypt(req);
//.....
}
这里的要点在[2]部分的检测上。
Linux中skb的分片分两种:
- 同一skb中的,多块非连续内存;
- 同一skb中的,多个skb;(主要存在于 分片了的网络数据包,未合并前的场景)
而 skb_has_frag_list 检测的是第二种情况,所以Dirty Frag只要构造第一种情况的分片就可以了。
可能有同学要问了,为什么不能走[1]?因为需要用 splice 来定位要攻击的文件缓存。
对 socket 做 splice 写时,会走 generic_splice_sendpage -> udp_sendpage 路径(udp socket);udp_sendpage 会将页缓存用 分片1 的方式加入到 skb 中。
而[1]线性的意思差不多是说,skb中不能有分片(实际含义是skb中的数据内存是连续的)。
最后,走到 crypto_aead_decrypt 时,只要前面设定好加密算法,就能直达 authencesn 模块。
2. esp4网络环境的搭建
在本地回环网络上搭建esp4网络环境,简单说只需要3步:
- 配置xfrm state
- 创建socket接收端
- 创建socket发送端
1. 配置xfrm state 网络数据包在进 esp4 之前,需要先经过 xfrm。
xfrm 负责转换报文,转换时会检查是否有匹配的状态。
ip xfrm state add \
src 127.0.0.1 dst 127.0.0.1 \
proto esp \
spi {ESP_SPI} reqid {ESP_REQID} mode transport \
encap espinudp 4500 4500 0.0.0.0 \
enc 'cbc(aes)' {aes_key.hex()} \
auth 'hmac(sha1)' {hmac_key.hex()} \
flag esn \
seq 0 replay-window 64 replay-seq 0 replay-oseq 0 replay-seq-hi 0 replay-oseq-hi 0
其中比较关键的参数:
- proto esp:指定使用 esp4 协议。
- encap espinudp:指定封装格式为 espinudp, udp包里放了esp包。
- flag esn:指定 esp 使用 authencesn 算法。
- reqid {ESP_REQID}:指定本地序列号的高4位——放Payload的地方(可参考 Copy Fail 相关文档)。
2. 创建socket接收端 接收端是不可或缺的,否则会被drop掉。
而且,需要配置上 UDP_ENCAP 选项,才能接收 esp4 包。
UDP_ENCAP = 100
UDP_ENCAP_ESPINUDP = 2
addr = ("127.0.0.1", ESP_PORT)
sk_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
sk_recv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk_recv.bind(addr)
sk_recv.setsockopt(socket.IPPROTO_UDP, UDP_ENCAP, UDP_ENCAP_ESPINUDP)
3. 创建socket发送端 发送端的关键之处在于要走pipe + splice。
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
sock.connect(addr)
r, w = os.pipe()
vmsplice(w, *****)
splice(fd, w, *****)
splice(r, sock.fileno(), *****)
4. 权限 实施过程中会发现,配置xfrm state 需要网络管理员权限。怎么办呢?
Linux中可以建立 UID/GID 映射,可以让自己伪装成 root 用户。
write_proc("/proc/self/uid_map", f"0 {uid} 1") # 命名空间 UID 0 → 真实 uid,范围 1
write_proc("/proc/self/gid_map", f"0 {gid} 1") # 命名空间 GID 0 → 真实 gid,范围 1
映射写成功后,命名空间内的 UID 0 既能获得完整 capability,又能操作外部属于该用户自己的文件。
但映射操作有一个前提,只能写映射尚未写入的全新用户空间。
所以就需要使用 Linux 的命令空间 clone 技术,先 clone 用户和网络 命令空间才行。
当然还有一些其他细节,比如:
- 新的网络空间的本地回环网络默认不开启;
- 设置gid_map前,必须要放弃 setgroups 能力
总体上前,Dirty Frag并没有太多新东西。
#
四、新的攻击路径
在看别人POC的时候,发现了一个之前一直忽略的问题。
Copy Fail明明用的 authencesn 模块,为什么官方要建议禁用 authenc 模块。
后来才明白,之所以公开的 POC一般用 authencesn,只有一个原因——简单方便,实现起来容易,能快速验证就可以了。
而 authenc 本身也存在攻击点——出入同源(这一点authencesn也存在),它的问题在于——只要知道点密码学知识,就可以控制解密结果。
拿 authenc 中的解密函数 crypto_authenc_decrypt 为例,其解密过程如下:
- 计算【关联数据+密文数据】的 验证hash值
- 与【验证数据】做比对,相同才做下面的解密
- 使用解密算法对【密文数据】进行解密
- 解密结果直接写到 输出源——也就是输入源
其中:
- 【密文数据】,是前面我们splice文件的内容,比如 /usr/bin/su
- 【关联数据】,在authenc中 =【spi】+【seq】+【iv】
- 【验证数据】,需要构造计算正确
完整的 esp 数据包 = 【spi】+【seq】+【iv】+【目标文件内容】+【验证数据】
还有几个隐含的内容,在 xfrm state 中设置。
- 验证算法
- 验证key
- 加密算法
- 加密key
其中,【验证数据】= 验证算法(验证key, 【spi】+【seq】+【iv】+【目标文件内容】)
剩下的内容,只有 加密算法、加密key 和【iv】会影响解密结果。其中前两者在 xfrm state 中配置,修改难度较大。相对【iv】修改起来就方便很多。
所以,最终要做的就是,找到一个算法,算出【iv】, 使【目标数据】= 加密算法(加密key,【iv】,【目标文件内容】)
密码学:AES-CBC —— AES算法的cbc模式
通常esp使用的加密算法都是 AES-CBC。
AES(Advanced Encryption Standard —— 高级加密标准),是国际上广泛采用的对称密钥加密算法。
其工作原理为:
- AES加密过程包括多轮轮换,每一轮使用不同的加密技术来转换数据。
- 在标准的AES中,加密过程分为三个阶段:初始轮、中间轮和最终轮。
- 初始轮包括字节替换、行移位和列混淆,中间轮由轮密钥加、字节替换、行移位和列混淆构成,而最终轮则不包含列混淆步骤。
- 这些复杂的转换保证了数据在加密后难以逆向还原。
- 每一轮中使用的轮密钥由初始密钥通过密钥扩展算法 生成,这一密钥扩展过程也是AES算法设计中的重要组成部分。
AES的CBC模式
CBC(Cipher Block Chaining —— 密码块链接)是一个利用反馈加密块的模式 —— 每个明文块在加密前,都需要与前一个密文块进行异或(XOR)操作。
这一过程产生了两个重要的特性:
- 由于这种XOR操作,即便同一明文块在不同的上下文中被重复使用,也会产生不同的密文块,从而提供了一定程度的隐藏明文的重复模式
- 加密过程中的第一个明文块没有前一个密文块可以与之异或,因此需要一个初始化向量(IV)来替代。
通过上面信息,可以明确一点,【iv】在 AES-CBC 的作用: 与 第一个数据块的解密结果 进行异或。
# 已知
Decrpt(key, data) XOR iv = Target
# 所以
iv = Target XOR Decrpt(key, data)
# 又因为 AES-CBC 必须要有初始 iv,需要 设置一个固定的 IV0
IV0 = 固定值
# 有
Decrpt(key, data) XOR IV0 = t0
# 所以
Decrpt(key, data) = t0 XOR IV0
# 所以
iv = Target XOR Decrpt(key, data)
iv = Target XOR (t0 XOR IV0)
用代码表示:
def compute_aes_cbc_iv(data: bytes, target: bytes, key: bytes) -> bytes:
"""
Compute IV for AES-CBC decryption to produce target bytes.
For the first block: P = AES_decrypt(C) XOR IV
So IV = AES_decrypt(C) XOR target
"""
iv0 = b"\x00" * AES_BLOCK_SIZE
t0 = aes_decrypt(key, data, iv0)
decrypt_data = xor_bytes(t0, iv0)
iv = xor_bytes(target, decrypt_data)
return iv
#
总结
本文是在分析DirtyClone(CVE-2026-43503)时写的,因手上的资源只有DirtyFrag的漏洞,就拿DirtyFrag作为了素材。
漏洞本身只是一个引子,authenc的这一块攻击路径,网上资料不多,同时也是我一直忽略的部分,所以就写了这篇文章,加深自己的认识。
以上内容仅为个人理解,如有错误,欢迎指正。
#
参考文献
V4bel/dirtyfrag
https://github.com/V4bel/dirtyfrag/blob/master/assets/write-up.md
nu11secur1ty/CVE-mitre
https://github.com/nu11secur1ty/CVE-mitre/blob/ac449b44513bd2f0f30db8cbb4c005f5fa38861e/2026/CVE-2026-43503/README.md
Dissecting and Exploiting Linux LPE Variant: DirtyClone (CVE-2026-43503)
https://research.jfrog.com/post/dissecting-and-exploiting-linux-lpe-variant-dirtyclone-cve-2026-43503/
AES加密技术的CBC模式详解
https://blog.csdn.net/weixin_42608318/article/details/149762449
AES加解密-CBC ECB
https://www.cnblogs.com/xzj8023tp/p/12970790.html
看雪ID:wanglxi
https://bbs.kanxue.com/user-home-485651.htm
*本文为看雪论坛优秀文章,由 wanglxi 原创,转载请注明来自看雪社区
第十届安全开发者峰会【议题征集】-欢迎投稿
往期推荐
一款可分享密码的解压缩 app 分析学习
一次外部注入工具的动态调试实录
从时间片轮转到调用级并发:unidbg 单后端多线程架构重构
基于VT EPT的无痕断点无痕hook原理及其应用
基于 GLM-5.2 的 X-Gnarly (332位) 纯算还原分析
球分享
球点赞
球在看
点击阅读原文查看更多
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:看雪学苑 wanglxi wanglxi《DirtyFrag浅析及一条新的攻击路径》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论