一个漏洞让云主机变肉鸡:聊聊 KVM 逃逸漏洞 Zapscape
一个漏洞让云主机变肉鸡:聊聊 KVM 逃逸漏洞 Zapscape
上个月你在 AWS 上开了台 EC2,隔壁租户也开了一台,你俩很可能就落在同一块物理机上。KVM 负责把你们隔开——他的代码碰不到你的内存,你的密钥他也看不见。理论上是这样。
2026 年 7 月 21 日之前,这个理论不成立。
安全研究员 Hyunwoo Kim(@v4bel)挖到一个 KVM 虚拟机逃逸漏洞,CVE-2026-64561,代号 Zapscape。攻击者租一台最便宜的机器就够了:从 guest 逃到 host,拿 root,然后同一台物理机上的其他租户跟着一起完蛋。
不是 PPT 漏洞。PoC 已经挂在 GitHub 上,能跑,能复现。
六年没人发现
影响范围从 2020 年 7 月 8 日的 commit f95eec9bed76,一直到 2026 年 7 月 21 日的修复 commit 2abd5287f083。整六年。
这六年里,所有开了嵌套虚拟化的 KVM/x86 主机都在裸奔,包括跑着数万台 VM 的公有云。
漏洞怎么工作:影子页表的一次 UAF
说人话。
KVM 用"影子页表"翻译 guest 的虚拟地址。L1 guest 里面又跑了一个 L2 guest(嵌套虚拟化)时,硬件 EPT/NPT 兜不住,KVM 得用软件额外维护一套影子页表。
每张影子页表对应一个结构体 kvm_mmu_page,两个字段是关键:
root_count:有多少个 vCPU 拿它当根页表。大于 0 说明有人在用,回收时得跳过。parent_ptes:指向父页表的指针。父页表没了,子页表也就可以回收了。
正常回收流程没毛病:内存不够 → 遍历 active_mmu_pages → 跳过 root_count > 0 的 → 回收剩下的。
问题出在另一条路径上。清理子页表时会走递归回收,而这条路径只看 parent_ptes 空没空,不看 root_count。
攻击者的骚操作是:让同一张影子页表 X 既当 child(某个嵌套页表的子节点),又当 root(另一个嵌套页表的根)。X 作为 child 的那个父页表被清掉时,递归回收顺手把 X 也回收了——哪怕 X 的 root_count > 0,哪怕还有 vCPU 正在用它。
use-after-free 就这么来了:页表已经 free 了,vCPU 还在往里写。
后面是一条挺讲究的提权链:两次 cross-cache 把 UAF 放大,拿到 KASLR 偏移,再串起内核的 log_wait、SRCU workqueue 和 usermode helper,最终在 host 上以 uid=0 落一个文件。PoC 落的是 /Zapscape,换成反弹 shell 也就多写几行。
打个比方:一栋写字楼,每家公司(VM)包几层,物业(KVM)规定没人用的会议室可以收回。但有个保洁(递归回收路径)从来不看里面是不是有人在开会,只看门牌上的公司名有没有被划掉。攻击者在同一间会议室挂了两块门牌,保洁看到其中一块划掉了,直接把正在开会的房间给拆了。然后攻击者从废墟里钻进机房,摸走了整栋楼的钥匙。
不只 AMD,Intel 也跑不掉
PoC 跑在 AMD SVM/NPT 上,因为 AMD 的嵌套虚拟化没有额外限制。但根因在 shadow MMU 的通用代码里,Intel 和 AMD 共用这份代码。
Intel 上同样能触发,条件是 L1 得同时暴露 EPT page walk length 4 和 5——这不是什么罕见配置。
最离谱的是权限那块:RHEL 这类发行版上 /dev/kvm 是 0666,任何用户可读写。也就是说根本不用虚拟机,一个普通用户就能拿它当本地提权用。这个默认值在我看来早就该改了,Zapscape 只是把账单送上门而已。
影响的不只是云厂商
直接受影响:
- 所有用 KVM 的公有云(AWS、GCP、Azure 都有 KVM 方案)
- 自建 KVM 虚拟化的企业
- 在 VM 里跑嵌套虚拟化的开发者(比如 VM 里跑 Docker 再跑 KVM)
间接的:业务跑在云上,你的安全边界就等于云厂商的补丁进度;做 SaaS 的,客户数据的安全上限由底层虚拟化决定,跟你自己代码写得多干净没关系。
v4bel 自己的说法:PoC 不是一键打云的武器,但"把 PoC 的逻辑挪进 guest 内核模块、再按目标 host 的 kconfig 适配一下,不是什么难事"。研究员说话客气,翻译过来就是:想干这事的人多半已经在干了。
先别慌,搞清楚自己暴露了多少
好消息:洞已经补了,7 月 21 日合入主线,各发行版陆续跟进。而且攻击者需要能在 guest 里跑嵌套虚拟化,不是每台 VM 都默认开着。
坏消息:补了不等于装了。公有云的内核更新周期不一定快,自建机房更慢,/dev/kvm 0666 在 RHEL 系上更是积弊多年。
更让我在意的是第三点:v4bel 之前还发过 Januscape(CVE-2026-53359),同样是 KVM 逃逸,同样出在 shadow MMU。一年之内、同一个人、同一个子系统,两个逃逸——这不像运气,更像这块代码根本没被认真审过。它在内核里跑了几十年,关注度却远不如那些新子系统。我赌后面还有。
今天能做的三件事
一、查内核版本
uname -r
没合入 7 月 21 日那个补丁就升级(PoC 打的是 7.1.3)。管着服务器的,yum update kernel 或 apt upgrade 走起。
二、关掉用不上的嵌套虚拟化
lsmod | grep kvm
你的 VM 真的需要在里面再跑一层 KVM 吗?不需要就关掉,攻击面能少一个是一个。
三、检查 /dev/kvm 权限
ls -la /dev/kvm
是 0666 就改成 0660 或更严。大部分应用压根不需要普通用户直接摸 KVM。
虚拟化不是魔法,隔离也不是绝对的。你信任的那层沙箱,本质上是六年前某个人漏写了一次 root_count 检查的产物。
现在就把上面三条跑一遍。跑完顺手想想:还有哪些"默认就安全"的东西,你其实从来没验证过。
参考来源:
✨ 本文由 DeepSeek 生成初稿,Claude 审核润色。