导语:一个引用计数下溢,一次1024次send调用,普通用户就能把自己变成root。这不是理论推演——Maher Azzouzi(马赫·阿祖齐)已经把完整可运行的vsockdrop PoC扔在了GitHub上,Ubuntu 22.04 HWE、24.04、26.04、Debian 13、Arch、openSUSE Leap/Tumbleweed全线阵亡。
一、漏洞本质
这个洞藏在Linux内核的vsock/virtio组件里,专挑zerocopy发送路径下手。当应用通过io_uring(内核异步I/O框架)注册一块FOLL_LONGTERM(长生命周期)的固定缓冲区后,缓冲区里的每个页面会被加上1024的引用计数偏置——这就是GUP_PIN_COUNTING_BIAS(全局固定页引用计数偏置),1024不是随便挑的数字,而是ABI常量。
io_uring走SEND_ZC(零拷贝发送)时,会把固定页挂到skb(socket buffer,网络缓冲区)上,但不取额外引用,只标记一个叫SKBFL_MANAGED_FRAG_REFS的flag,意思是”这块页归io_uring管,网络栈别动它”。本意是好的,网络栈在释放skb时看到这个flag就会跳过put_page(页面引用释放),避免双重释放。
问题出在vsock的大消息路径上。当payload超过64K被拆成多个skb发送时,这个flag只被设置在最后一个skb上,前面那些非最终skb可不管这套,照样老老实实执行put_page。可问题是,这些页从来没被取过引用——io_uring没拿,网络栈也没拿——put_page直接作用在1024那个pin偏置上,每次send多减一次。
攻击者只要控制vsock连接让每次send只触发一次下溢,连续1024次下来,pin归零,被固定的页面就掉进了每CPU空闲页链表(PCP freelist)。到这里还没完,攻击者还有更脏的招数等着。
二、五步拿root
Maher Azzouzi的exploit把这条链子拆成了五个干净的步骤,没有花哨技巧,全是硬核的内核态理解。

第一步:注册固定缓冲。 io_uring_register_buffers把一块192K的mmap(内存映射)区域登记进ring(异步I/O环形队列),内核给每页打上FOLL_LONGTERM标记,refcount变成1025(1是原始引用 + 1024是pin偏置)。
第二步:munmap丢弃用户态映射。 解除页表映射后,页面唯一的”人质”就是那个pin偏置。此时_mapcount(映射计数)已经是-1,意味着页面绕过free_page_is_bad检查,可以直接进PCP freelist。
第三步:1024次vsock zerocopy send。 接收端配合同步:每次只drain(排空)一个send的数据并发回ack,让发送端每次submit只产生一次skb frag(缓冲区片段)下溢。锁步执行,1024次后,pin归零,页面脱离。
第四步:冷读取/usr/bin/su第0页。 这一步最黑。fadvise驱逐su的page cache(页缓存)后,单次pread(带偏移读)会触发新页分配。新分配的页就来自刚才被bug释放的PCP顶——LIFO(后进先出)原则,攻击者的”幽灵页”摇身一变成了su的ELF(Linux可执行文件格式)头页。
第五步:重写PT_INTERP(程序解释器段)。 通过io_uring的read_fixed/write_fixed(基于固定缓冲区的读写),不需要用户PTE(页表项),直接改su的.interp字符串指向/tmp/loader。然后exec su——内核作为root把攻击者的loader当作动态链接器加载执行。loader不主动开shell(那样会触发execve时页面还在PCP里的7.0内核崩溃),它只把/var/tmp/.s刷成setuid-root(设置用户ID为root的特殊权限位)。等exploit worker退出,ring和pin都拆干净,干净的父进程exec这个setuid helper,真正的root shell弹出来。
整套动作不需要user namespace(用户命名空间),不需要任何内核模块,一条静态二进制,谁跑谁是root。
三、PoC核心代码
完整exploit在github.com/maherazzouzi/vsockdrop,包含exploit.c、interp_data.h(loader二进制)、rootsh_data.h(setuid helper二进制)。下面是核心send循环:
/* drain the pin to 0 -> the still-pinned page lands on the PCP freelist */
for (int i = 0; i < iters; i++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
/* buf is unmapped; base still passes io_import_fixed's range check */
io_uring_prep_send_zc_fixed(sqe, s, base, SEND_SIZE, MSG_WAITALL, 0, 0);
io_uring_submit(&ring);
struct io_uring_cqe *cqe;
for (int j = 0; j < 2; j++) {
if (io_uring_wait_cqe(&ring, &cqe) < 0)
break;
io_uring_cqe_seen(&ring, cqe);
}
char a;
(void)!read(ack_r, &a, 1); /*wait for the drain before next send */
}
/* one cold single-page read pops our last-freed PFN off the PCP top
as su's page0 (the page that carries the ELF header + .interp) */
int sufd = open("/usr/bin/su", O_RDONLY);
if (sufd >= 0) {
posix_fadvise(sufd, 0, 0, POSIX_FADV_RANDOM);
char one_pg[4096];
(void)!pread(sufd, one_pg, sizeof(one_pg), 0);
close(sufd);
}
这里几个细节值得拆:SEND_SIZE取68K(64K+4K),刚好够触发多skb路径又足够小让接收端一次drain完;send循环里base指向已经munmap的虚拟地址,但io_import_fixed只校验范围不看PTE,固定缓冲注册还在;ack管道是关键,没它的话TCP流控会让sender重发,下溢次数对不上。
.interp的patch是整条链里最精巧的一段:
char *interp = find_interp_str(page);
memcpy(interp, INTERP_PATH, len);
memset(interp + len, 0, room - len);
msync(snap, BUF_SIZE, MS_SYNC);
攻击者通过mmap一份su的页缓存snap.bin,定位到/lib64/ld-linux这类解释器路径,整段覆盖成/tmp/loader,msync刷回磁盘。回到内核视角,su的PT_INTERP段指向了攻击者控制的二进制——而这一切发生在攻击者从未提权之前。
四、影响与修复
影响范围:Linux 6.7(漏洞引入)到 7.0.10,跨度14个版本。Ubuntu 22.04 HWE、24.04、26.04,Debian 13,Arch,openSUSE Leap/Tumbleweed全部中招。
CVSS争议:NVD给了5.5分,理由是”可用性问题”。漏洞作者直接开喷——这是一个未经认证的本地提权(LPE),应该按7.8评分。NVD这种评分思路,给攻击者省了不少事。
修复版本:Linux 7.0.11。上游commit改了两件事:在send循环开始前就把uarg(用户态关联参数)分配好,然后用skb zcopy set()函数把uarg挂到每一个skb上,确保非最终skb也能正确追踪完成、避免pin泄漏。
检测要点:
- 内核日志出现vsock释放路径相关的warning或stack trace
- 系统监控到大量SO_ZEROCOPY vsock连接 + io_uring固定缓冲注册的组合
- /usr/bin/su的mtime(修改时间)异常变动
- /var/tmp/.s等可疑setuid文件落地
缓解建议:
- 升级到Linux 7.0.11或对应发行版的安全补丁
- 如果暂时无法升级,限制非特权用户使用io_uring(sysctl kernel.io_uring_disabled=2)
- 部署audit规则监控setuid文件创建和/usr/bin/su的完整性变化
五、总结
vsockdrop最让我佩服的地方,不是漏洞本身有多新颖,而是每一步都在Linux内核的”正常行为”里完成。1024是ABI常量、put_page是路径上每个skb都会执行的、page cache的LIFO回收是教科书式的、PCP freelist是性能优化——所有这些机制单独看都合理,串起来就是一个完整的提权链。
这就是内核攻击的真正美学:不是发明了什么新技术,而是把现有机制的边界摸得比开发者还清楚。











暂无评论内容