CVE-2026-53359(Januscape):潜伏16年的 KVM/x86 客户机逃逸漏洞分析与修复
披露时间:2026-07-04(CVE 分配)/ 2026-07-06(公开)
危害等级:🔴 Critical(严重)
漏洞类型:Use-After-Free(CWE-416)→ Guest-to-Host Escape
影响组件:Linux KVM x86 Shadow MMU(arch/x86/kvm/mmu/mmu.c)
潜伏时间:约 16 年(自 2010 年 commit2032a93d66fa起)
架构范围:Intel VMX + AMD SVM(双架构)
发现者:Hyunwoo Kim(@v4bel)
一、漏洞概述
2026 年 7 月,安全研究员 Hyunwoo Kim(@v4bel) 在 oss-security 与 Januscape 项目 中公开了 CVE-2026-53359,并将其命名为 Januscape——一枚可在 KVM/x86 上实现 Guest-to-Host Escape 的 Use-After-Free 漏洞。
据目前公开资料,这是首个可同时在 Intel 与 AMD 上触发的 guest-to-host 逃逸研究案例(不依赖单一厂商扩展指令路径)。漏洞位于内核 KVM 的 Shadow MMU 仿真逻辑中:客户机侧操作即可破坏宿主机 shadow page 状态,进而威胁接受不可信客户机、并暴露嵌套虚拟化(nested virtualization) 的多租户 x86 公有云(如 GCP、AWS 等)。
该漏洞曾作为 0-day 在 Google kvmCTF 中成功利用。
| 属性 | 详情 |
|---|---|
| CVE 编号 | CVE-2026-53359 |
| 别名 | Januscape |
| 漏洞类型 | Use-After-Free / 数据损坏 → DoS / Host Escape |
| 触发面 | 嵌套虚拟化下的 Shadow MMU(非 QEMU 用户态模拟) |
| 权限要求 | 客户机内 root(云实例通常满足) |
| arm64 | 不受影响(x86 专用) |
二、背景:为什么 Shadow MMU 会成为攻击面?
x86 KVM 将客户机虚拟地址翻译到宿主机物理地址,主要有两条路径:
-
硬件二级页表(TDP)
Intel EPT / AMD NPT 由硬件完成 GPA→HPA 二级翻译。现代主机默认启用(kvm-intel.ept=1、tdp_mmu=Y),走 TDP MMU。 -
Shadow Paging(影子页表)
KVM 在软件中“镜像”客户机页表,维护struct kvm_mmu_page(其后端是 4KB 的sp->spt)。
嵌套虚拟化是关键开关:当 L1 客户机本身充当 Hypervisor、再用 EPT/NPT 运行 L2 时,硬件只有一级“二级页表”,因此 L0(宿主机)必须用软件 Shadow 掉 L1 为 L2 构建的嵌套页表。这条路径走的是 legacy Shadow MMU,而非 TDP MMU——Januscape 正发生在这里。
更重要的是:这一切发生在 in-kernel KVM 中,不经过 QEMU 等用户态 VMM。因此它不是“常见 QEMU 逃逸”,而是纯内核 KVM 缺陷,对自研虚拟化栈的公有云同样危险。
KVM 还为每个 memslot 维护 rmap(reverse map):按 gfn 反向追踪 leaf SPTE。安装 leaf 时把 SPTE 指针加入该 gfn 的 rmap,拆除时再按同一 gfn 键移除。
install 与 teardown 看到的 gfn 键必须一致——Januscape 正是打破了这一不变量。
三、根因:kvm_mmu_get_child_sp() 只比 gfn、不比 role
补丁前的关键函数(arch/x86/kvm/mmu/mmu.c):
static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu,
u64 *sptep, gfn_t gfn,
bool direct, unsigned int access)
{
union kvm_mmu_page_role role;
if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) &&
spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn)
return ERR_PTR(-EEXIST); /* 仅比较 gfn,忽略 role */
role = kvm_mmu_child_role(sptep, direct, access);
return kvm_mmu_get_shadow_page(vcpu, gfn, role);
}
复用判定只看子 shadow page 的 gfn,不看 role。
role.direct 区分两类页面:
| role.direct | 含义 |
|---|---|
0(indirect) | 真正 shadow 了 guest 页表 |
1(direct) | 大页无法按原样映射时,KVM 拆成 4KB 的 direct split 页 |
在 fetch 路径中,同一 gfn 既可能走 indirect 创建,也可能走 direct split——仅比 gfn 就会在 role 不匹配时错误返回 -EEXIST,复用错误角色的旧页。这就是 Januscape 的根因。
Use-After-Free 路径
错误复用会打乱 shadow page 的生命周期与 parent 指针记账,最终出现:某 shadow page 已释放,仍有父页持有指向它的指针(orphaned parent pointer)。清理时:
__kvm_mmu_prepare_zap_page()
→ kvm_mmu_unlink_parents()
→ drop_parent_pte()
→ mmu_spte_clear_no_track()
→ WRITE_ONCE(*sptep, SHADOW_NONPRESENT_VALUE) // x86-64: BIT_ULL(63)
内核以为在“清空 shadow page 的一个槽位”,若该页已释放并被其他内核对象占用,这次写入就变成 UAF write。客户机可控制写入的 offset(槽位),写入值固定。
DoS 路径(公开 PoC)
公开 PoC 走更简单的完整性崩溃路径:
- 被复用的 direct 页没有
shadowed_translation,leaf 的 gfn 按sp->gfn + index计算; - 安装时 rmap 记在真实 guest gfn 下,拆除时却按计算 gfn 查找;
pte_list_remove()发现 rmap 不一致,触发KVM_BUG_ON_DATA_CORRUPTION;- 在
CONFIG_BUG_ON_DATA_CORRUPTION=y(如 RHEL 默认)且panic_on_oops场景下,宿主机直接 panic。
典型 panic 日志片段:
gfn mismatch under direct page 8a00 (expected 8b00, got 256e4)
WARNING: ... kvm_mmu_page_set_translation ...
kernel BUG at arch/x86/kvm/mmu/mmu.c:1091!
RIP: 0010:pte_list_remove ...
Comm: qemu-kvm
四、触发条件与影响范围
触发前提
| 条件 | 说明 |
|---|---|
| x86 宿主机 | Intel 或 AMD,运行 KVM |
| 嵌套虚拟化 | 实例暴露 nested virt(L1 可再开 L2) |
| 未打补丁内核 | 见下方版本表 |
| 客户机权限 | 通常需 guest root(加载模块 / 直接操控 VMX/SVM) |
在默认 ept=1、tdp_mmu=Y 的主机上,普通 GPA 翻译走 TDP,不经过 kvm_mmu_get_child_sp;但一旦 L1 运行嵌套客户机,L0 必须 shadow L1 的 nested EPT/NPT,仍会落入该路径。
受影响版本
漏洞存在区间(git):2032a93d66fa(2010-08-01)→ 81ccda30b4e8(2026-06,修复)。
语义版本上,自 Linux 2.6.36 起受影响,直至下列安全版本(含后续):
| 分支 | 安全版本(≥) |
|---|---|
| 6.1 | 6.1.177 |
| 6.6 | 6.6.144 |
| 6.12 | 6.12.95 |
| 6.18 | 6.18.38 |
| 7.1 | 7.1.3 |
| mainline | 7.2-rc1 及含补丁的后续版本 |
危害场景
-
KVM Escape
仅租一台公有云实例,即可 panic 同物理机上所有租户(DoS),或在完整利用链下以 host root 接管宿主机及全部客户机(RCE/Escape)。 -
本地提权(次要)
部分发行版(如 RHEL)/dev/kvm为0666,无特权用户也可能把该洞当作可靠 LPE。 -
与 QEMU 无关
缺陷在内核 KVM,对不用 QEMU 的云厂商栈同样适用。
五、利用思路(高层,防御视角)
公开 PoC 是客户机内内核模块:在 L1 内用 raw VMX(Intel)或 SVM(AMD)自建 L2,迫使 L0 对 L1 nested 页表做 shadow,并在 race 窗口触发 role 复用。
核心几何:同一物理页既充当大页 leaf,又充当页表页,使 gfn 相同、role 不同。
- Writer vCPU 在 huge PDE 与 table PDE 之间来回切换;
- Faulter vCPU 反复跑 L2 以制造经过该 PDE 的 fault;
- L0 模拟 PDE 写入时,commit 新值与 zap 旧 shadow 链路之间存在非原子窗口;
- 窗口内
get_child_sp仍链着旧 role 子页,却按新 role 请求 → 仅 gfn 匹配即复用。
完整逃逸原语更刁钻:UAF 写值固定(SHADOW_NONPRESENT_VALUE),攻击者只控 offset,需跨 cache 选准 victim 对象;Intel/AMD 利用路径不同。完整 escape exploit 作者暂未公开。
⚠️ 请勿在未授权环境加载任何 PoC 模块。 本文仅作防御与原理说明。
六、修复方案
方案一:升级内核(强烈推荐 ⭐)
主线补丁 commit:81ccda30b4e8
核心改动:复用条件增加 role.word 比较。
- if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep) &&
- spte_to_child_sp(*sptep) && spte_to_child_sp(*sptep)->gfn == gfn)
+ union kvm_mmu_page_role role = kvm_mmu_child_role(sptep, direct, access);
+
+ if (is_shadow_present_pte(*sptep) &&
+ !is_large_pte(*sptep) &&
+ spte_to_child_sp(*sptep) &&
+ spte_to_child_sp(*sptep)->gfn == gfn &&
+ spte_to_child_sp(*sptep)->role.word == role.word)
return ERR_PTR(-EEXIST);
role 不同时不再复用,改为创建正确角色的新 shadow page,rmap 损坏与 UAF 路径随之消失。
发行版升级建议:
# 确认当前内核
uname -r
# Debian / Ubuntu(示例:关注 security 通道)
sudo apt update && sudo apt full-upgrade
# RHEL / Alma / Rocky
sudo dnf update kernel
# 重启进新内核
sudo reboot
云厂商请优先应用 host 侧 内核补丁(客户机升级无法修复宿主机 KVM)。
方案二:临时缓解(无法立即升级时)
| 措施 | 说明 | 代价 |
|---|---|---|
| 关闭嵌套虚拟化 | 对不可信租户禁用 nested virt | 无法在 VM 内再开 VM |
| 限制 /dev/kvm | 收紧权限,避免本地 LPE | 不影响远程租户逃逸主路径 |
| 租户隔离 | 关键工作负载独占物理机 / 使用机密计算 | 成本上升 |
| 监控 | 关注 qemu-kvm 异常、pte_list_remove / gfn mismatch 日志 | 检测滞后 |
临时缓解不能替代补丁:只要暴露 nested virt 且内核未修,恶意租户仍可攻击 host。
方案三:确认补丁是否已合入
# 在内核源码树中
git log --oneline --grep='unexpected role' arch/x86/kvm/mmu/mmu.c
# 或检查是否包含 81ccda30b4e8 及其 stable 回移植
# 运行中的系统可对照厂商 changelog / CVE 公告
rpm -q --changelog kernel | grep -i 53359 # RPM 系示例
七、披露时间线
| 时间 | 事件 |
|---|---|
| 2026-06-12 | 向 security@kernel.org 提交漏洞细节与利用 |
| 2026-06-13 | 与 KVM 维护者 Paolo / Sean 讨论补丁方案 |
| 2026-06-17 | Paolo 将补丁发至 lore 测试 |
| 2026-06-19 | 补丁 81ccda30b4e8 合入 mainline |
| 2026-07-01 | 提交 linux-distros,约 5 天 embargo |
| 2026-07-04 | 分配 CVE-2026-53359 |
| 2026-07-06 | embargo 结束,oss-security 与 Januscape 文档公开 |
八、总结与行动清单
Januscape 再次说明:虚拟化隔离边界上的“小不变量”(gfn + role)一旦被打破,后果可以是整机失陷。它潜伏约 16 年,跨 Intel/AMD,且不依赖 QEMU——对公有云与自建 KVM 集群都是高优先级补丁。
立即行动清单
- 1. 资产盘点:列出所有 x86 KVM 宿主机(含 OpenStack、Proxmox、自建云、裸金属 KVM)。
- 2. 版本核对:对照 6.1.177 / 6.6.144 / 6.12.95 / 6.18.38 / 7.1.3 等安全版本。
- 3. 优先补 host 内核:客户机升级不能替代宿主机修复。
- 4. 评估 nested virt:对不可信租户默认关闭嵌套虚拟化。
- 5. 收紧 /dev/kvm:避免
0666带来的本地提权面。 - 6. 日志监控:关注
gfn mismatch under direct page、pte_list_remove、突发qemu-kvmpanic。 - 7. 关联加固:同步检查 arm64 上的 ITScape (CVE-2026-46316) 等兄弟漏洞是否已修。
参考资源
- NVD – CVE-2026-53359
- oss-security 公告(Hyunwoo Kim)
- Januscape 项目(GitHub)
- 技术 write-up
- 主线修复 commit 81ccda30b4e8
- Google kvmCTF 介绍
⚠️ 免责声明:本文仅供安全研究与防御参考。请勿在未授权系统上测试或利用该漏洞。请尽快为 KVM 宿主机应用官方内核补丁,保护多租户隔离与生产环境安全。
撃っていいのは撃たれる覚悟のあるヤツだけだ。