CVE-2026-43494 / PinTheft:Linux RDS 与 io_uring 组合出的本地提权风险

整理 CVE-2026-43494 / PinTheft 的核心信息:它如何由 Linux RDS zerocopy 引用计数问题与 io_uring fixed buffer 组合成本地提权链,以及管理员应如何判断前置条件、降低暴露面并等待发行版修复。

CVE-2026-43494 是一个 Linux 内核本地权限提升风险,外界也用 PinTheft 来称呼相关利用链。它的关键不在于远程入口,而在于本地低权限用户能否同时碰到 RDS zerocopy、io_uring fixed buffer、可读 SUID-root 程序和合适的内核版本。

需要先说明一个编号细节:Unclecheng-li/poc-lab 仓库目录名写的是 CVE-2026-43494 PinTheft,README 标题里又写了 QVD-2026-27616 — PinTheft。从公开 CVE 条目和第三方通告看,CVE-2026-43494 指向的是 Linux kernel RDS zerocopy 中 op_nents 未正确重置引发的 double-free / 引用计数异常问题;QVD-2026-27616 更像是奇安信风险通告编号。实际排查时建议同时记录这两个标识,但以发行版安全公告和内核补丁状态为准。

漏洞核心是什么

这个问题出现在 Linux RDS,也就是 Reliable Datagram Sockets 的 zerocopy 发送路径中。公开描述里的关键函数是:

1
2
rds_message_zcopy_from_user()
rds_message_purge()

iov_iter_get_pages2()rds_message_zcopy_from_user() 中失败时,已经 pin 住的页面会先被错误路径释放;但相关 op_nents 信息没有被正确清零,后续 rds_message_purge() 仍可能按残留条目再释放一次。结果就是同一批页面引用被多减,形成可被利用的引用计数错误。

单看 RDS bug,它是内核内存管理里的错误路径问题。PinTheft 的危险之处在于利用链把它和 io_uring 固定缓冲区机制连了起来:io_uring 仍保存着旧的 struct page *,而页面本身已经被释放并重新分配给其他用途。PoC 进一步把这个状态引向 SUID-root 程序的 page cache 覆写,最终达到本地提权。

为什么叫 PinTheft

io_uring REGISTER_BUFFERS 会固定用户页。对普通页面来说,FOLL_PIN 并不只是简单增加一个引用,而是通过较大的 bias 增加 page refcount。公开 PoC 中使用了 GUP_PIN_COUNTING_BIAS = 1024 这个概念。

PinTheft 这个名字的意思就是:攻击链通过 RDS zerocopy 的失败路径,一次次“偷走”这些 pin 引用。等引用被耗掉后,io_uring 仍以为自己持有有效页面,但该物理页已经可以被释放并被 page cache 重新占用。

这类漏洞容易被误读成“直接改了磁盘上的 /usr/bin/su”。更准确的说法是:利用链尝试覆写的是内存中的 page cache。文件本体不一定被写回磁盘,但内核执行该 SUID 程序时可能从被污染的页缓存取指令,从而运行攻击载荷。

触发条件并不宽

这不是一个“任意 Linux 服务器都能远程打”的漏洞。公开信息显示,利用链至少依赖这些条件:

  • 内核启用了 CONFIG_RDSCONFIG_RDS_TCP
  • 系统启用了 CONFIG_IO_URING,且 kernel.io_uring_disabled=0
  • rds / rds_tcp 模块已经加载,或低权限用户可以触发自动加载。
  • 本地存在可读的 SUID-root 二进制程序,例如 /usr/bin/su/usr/bin/passwd/usr/bin/pkexec
  • 公开 PoC 还依赖较新的 IORING_REGISTER_CLONE_BUFFERS API,CloudLinux 的分析提到公共 PoC 更偏向 kernel 6.13 及以上形态。

只要其中一环不成立,公开链路就会断掉。比如很多 RHEL 系发行版默认不编译 RDS,Ubuntu 旧内核可能缺少 PoC 需要的 io_uring clone buffer API,部分环境也会限制非特权用户自动加载 RDS 模块。

一分钟自查

先看内核配置:

1
2
zgrep -E "CONFIG_(RDS|RDS_TCP|IO_URING)" /proc/config.gz 2>/dev/null \
  || grep -E "CONFIG_(RDS|RDS_TCP|IO_URING)" /boot/config-$(uname -r)

再看 io_uring 是否被禁用:

1
cat /proc/sys/kernel/io_uring_disabled 2>/dev/null

常见含义可以按这个思路理解:

  • 0:允许使用,风险面最大。
  • 1:限制非特权用户使用,具体行为看内核版本和发行版策略。
  • 2:禁用 io_uring

检查 RDS 模块是否存在、是否可加载:

1
2
lsmod | grep -E "^rds|^rds_tcp"
modprobe -n -v rds_tcp 2>&1 | head -3

如果 CONFIG_RDSnot set,或者系统根本没有 rds_tcp 模块,通常就无法走到这个 bug。反过来,如果 RDS 可用、io_uring 未禁用、系统又是较新的通用内核,就应该提高优先级继续确认发行版修复状态。

哪些机器更值得优先看

优先排查这些环境:

  1. 多用户 Linux 主机、教学机、跳板机、共享开发机。
  2. 容器宿主机,尤其是允许不可信本地用户或较宽松容器逃逸面的环境。
  3. 开启较新 mainline / rolling kernel 的桌面或服务器,例如 Arch 一类滚动发行版。
  4. HPC、Oracle RAC 或其他可能真实使用 RDS 的场景。
  5. 允许非特权用户运行大量本地代码的 CI worker、构建机和实验环境。

普通 Web 服务如果只有受控服务账号运行应用,并且 RDS 未启用,实际风险会低很多。但“低很多”不等于不用处理:内核本地提权的典型影响是攻击者先通过 Web、SSH、CI、容器或应用漏洞拿到低权限,再用本地提权扩大控制面。

临时缓解建议

正式修复仍应以发行版内核更新为准。补丁、回溯版本和受影响范围需要看 Debian、Ubuntu、RHEL、AlmaLinux、Rocky Linux、SUSE、Arch、云厂商或容器基础镜像各自的安全公告,不要只按上游版本号判断。

在等待补丁或无法马上重启内核时,可以按环境选择临时措施:

1
2
3
4
5
# 如果业务不依赖 RDS,可阻止相关模块加载
sudo sh -c "printf 'install rds /bin/false\ninstall rds_tcp /bin/false\ninstall rds_rdma /bin/false\n' > /etc/modprobe.d/pintheft.conf"
sudo rmmod rds_tcp 2>/dev/null
sudo rmmod rds_rdma 2>/dev/null
sudo rmmod rds 2>/dev/null

如果业务不依赖 io_uring,也可以考虑禁用或限制它:

1
sudo sysctl -w kernel.io_uring_disabled=2

持久化配置需要写入对应的 /etc/sysctl.d/*.conf。不过这一步要谨慎,现代数据库、代理、运行时或高性能 I/O 程序可能使用 io_uring;生产环境修改前最好先确认业务依赖。

修复后怎么验证

升级内核后,不要只看包管理器输出成功。建议确认三件事:

1
2
3
uname -a
cat /proc/sys/kernel/io_uring_disabled 2>/dev/null
modprobe -n -v rds_tcp 2>&1 | head -3

如果发行版公告明确写明已修复 CVE-2026-43494,即使 uname -r 看起来不是最新上游版本,也可能是已经回溯补丁的稳定内核。反过来,如果内核来源是自编译、第三方仓库、云市场镜像或容器宿主机模板,就要继续核对补丁 commit 和构建时间。

参考信息

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计