跳到正文
认知
返回认知精选
认知精选科技大约 5 小时前3
单一来源当前只有一个可用来源,尚未完成多源交叉核验

Firecracker VM逃逸:PwnEscape第二部分

Firecracker 的设计初衷是将不受信任的代码隔离在 microVM 内部。我们成功逃逸,并在宿主机上以 root 权限实现了代码执行。

我们发现了它保存寄存器的方式、更改恢复执行位置的方法,以及在从 KVM 返回时执行我们选定的调用的手段。随后,客机(guest)恢复了临时的栈改动。Firecracker 继续运行,且虚拟机依然可以通过 SSH 访问。

PwnEscape 第一部分介绍了支撑这项工作的宿主机内存原语。在本文中,我们继续攻击链的下一部分:利用该访问权限在内存中找到 Firecracker,并从客机内部接管一个活跃的线程。这是在 Firecracker 设置上展示 CVE-2026-99999 的后续内容。

00 / 选择控制权接管的位置

一个 Firecracker microVM 在宿主机上有一个管理它的进程。每个虚拟 CPU(即 vCPU)在该进程中都有一个线程。该线程进入 KVM 以运行客机,并在需要在用户空间处理事件时返回 Firecracker。Firecracker 在其架构概览(architecture overview)中记录了这种安排。

运行虚拟 CPU 的调用看起来像这样:

ioctl(vcpu_fd, KVM_RUN, 0);

当线程处于该系统调用中时,宿主机内核将其保存的用户空间寄存器保存在一个名为 pt_regs 的结构体中。这些寄存器描述了 Firecracker 线程:其指令指针(instruction pointer)指示了执行应从何处恢复,其栈指针(stack pointer)指示了它应使用哪个用户空间栈。

这使得从 KVM_RUN 的返回成为一个有吸引力的目标。如果我们能够定位并修改保存的栈帧(frame),我们就可以选择线程返回用户空间时 Firecracker 执行的代码。引发这种返回的客机操作将提供触发机制。

我们已经通过客机映射获得了对选定宿主机物理内存的访问权限。为了使用它,我们需要识别 Firecracker 进程,找到我们可以在该进程中调用的代码,并定位其活跃 vCPU 线程所属的寄存器帧。起点是我们在客机内部控制的一个内存页面。

图 1. 客机识别其 Firecracker 进程,找到 vCPU 线程保存的寄存器,并在线程从 KVM 返回时重定向该线程。

01 / 在宿主机上寻找我们的客机页面

客机和宿主机使用不同的物理地址空间。一个页面在虚拟机内部具有客机物理地址(Guest Physical Address,简称 GPA),在支持它的机器中具有宿主机物理地址(Host Physical Address,简称 HPA)。在目标机器上,扩展页表(Extended Page Tables,简称 EPT)提供了它们之间的转换。

我们将一个可识别的字节模式(即一个 nonce)放入客机页面中。第一个解析器将该页面的已知 GPA 及其内容作为输入。它们共同为我们提供了一个要转换的地址,以及一种检查转换是否到达我们页面的方法。

解析器在内存原语所覆盖的宿主机物理范围内搜索候选的 EPT 根(EPT roots)。EPT 根是转换表的起点。对于每个候选者,解析器沿着由客机地址选定的表项进行查找,直到到达一个物理页面,然后检查该页面是否存在该 nonce。

以下是该文章内容的中文翻译:

下面的代码片段是带有描述性辅助名称的解释性伪代码。它们展示了阶段之间传递的数据以及用于将它们连接起来的检查。

for root in candidate_ept_roots: page_hpa = walk_ept(root, guest_gpa) if page_hpa is invalid: continue

contents = read_host(page_hpa, length(nonce)) if contents == nonce: candidates.append((root, page_hpa))

随机数(nonce)检查将建议的转换与我们控制的内存连接起来。更广泛的搜索产生了两个候选对象;对嵌套页表(nested page tables)的验证将它们减少到一个 EPT 根(EPT root)。遍历该根给我们提供了客户机页面的宿主机物理地址。

我们将这个结果称为 guest_page_hpa,并将其传递给下一个解析器。我们现在在宿主机内存中拥有了一个可识别的对象。接下来的任务是找到映射它的宿主机进程。

02 / 寻找 Firecracker 进程

Firecracker 将客户机使用的内存映射到其自己的地址空间中。这种映射为我们提供了一种从客户机页面反向推导到进程的方法。

宿主机进程使用页表将其虚拟地址转换为物理内存。最高级别被称为页全局目录(page global directory,简称 PGD)。恢复适当的 PGD 将使我们能够转换 Firecracker 内部的地址,并通过内存原语(memory primitive)读取其代码和栈。

我们的反向映射器(reverse mapper)在本地宿主机页表中搜索 guest_page_hpa 的映射。正常的页表遍历从虚拟地址开始并产生物理地址。在这里,我们寻找指向已知物理页面的映射,然后恢复与之关联的虚拟地址和页表根。

该检查双向运行:

for mapping in reverse_map_host_page_tables(guest_page_hpa): pgd = mapping.root guest_page_va = mapping.virtual_address

resolved_hpa = walk_host_page_tables(pgd, guest_page_va) if resolved_hpa != guest_page_hpa: continue

if read_host(resolved_hpa, length(nonce)) != nonce: continue

process_candidates.append((pgd, guest_page_va))

这把宿主机进程的虚拟映射与通过 EPT 找到的物理页面连接起来。反向映射器返回了一个经过验证的地址空间。我们保留其根作为 host_pgd,并用它来检查该进程。

下一个解析器在这些映射中寻找 Firecracker 可执行文件。它识别出一个 ELF 镜像(ELF image),并检查了 Firecracker 的 write(2) 包装器中的一个已知字节序列。这把恢复的地址空间与我们目标定位的二进制文件连接了起来。

地址空间布局随机化(ASLR)改变了可执行文件的加载位置。ELF 镜像中的地址与其对应的运行时地址之间的差值就是加载偏移(load bias)。一旦映射器识别出该镜像,它就可以恢复该偏移,并用它来定位漏洞利用所需的代码:

runtime_code_address = load_bias + code_address_in_elf

映射器将加载偏移(load bias)返回为 firecracker_base。有了该值和 host_pgd,我们便能在运行中的进程中定位代码,将其虚拟地址转换为宿主机物理内存,并将该处的字节与期望的二进制文件进行对比。

我们检查了三个位置:将产生我们的证明的 write(2) 包装函数、KVM ioctl 之后的延续位置,以及干净返回所需的简短 xor eax,eax; ret 序列。当我们在下方构建返回路径时,它们的用途将变得相关。

此时,客机(guest)可以检查 Firecracker 镜像及其私有映射。我们仍然需要当前运行我们 VM 的线程所保存的寄存器。

图 2. 随机数(nonce)用于识别宿主机内存中的客机页面。其映射指向宿主机页表根目录,这使得下一个解析器能够定位并验证 Firecracker 镜像。

03 / 定位实时 vCPU 帧

帧查找器从前几个阶段获取 host_pgd 和 firecracker_base。它利用这些信息来识别宿主机线程保存的状态。

最有用的线索是返回地址。KVM_RUN 中的 vCPU 线程应该在 Firecracker 中相应的 ioctl 延续位置处具有保存的指令指针。我们可以从已验证的二进制文件及其加载偏移计算出该地址。

然后,我们在宿主机内存中搜索与该调用相符的 pt_regs 帧。仅凭地址匹配也会捕获到陈旧的栈内容,因此候选对象必须满足几个相关的检查:

保存的状态我们检查的内容

系统调用系统调用标识和进行中的状态与活动中的 ioctl 相匹配。

请求参数ioctl 请求为 KVM_RUN。

文件描述符描述符落在辅助程序(helper)的预期范围内。

指令指针它与已验证的 Firecracker ioctl 延续位置相匹配。

栈指针它通过恢复的宿主机 PGD 进行转换。

段选择器与标志它们描述了向用户空间的有效返回。

调用者返回地址它落在了已验证的 Firecracker 文本映射内部。

栈检查将该帧与我们从客机页面恢复的地址空间绑定在一起。指令地址和调用者地址将其与 Firecracker 镜像绑定。系统调用字段将其与运行 vCPU 的操作绑定。

搜索只留下了一个候选对象。它的位置

Evidence

来源与证据

单一来源 · 1 个信源

当前仅有一个可用来源,尚未完成多源交叉核验。

发布时间
内容处理
AI 翻译 / 摘要,需结合原文核对
内容有误或来源失效?告诉我们

每天早上收到这 5 条

免注册,填邮箱即可;每封邮件都能一键退订。

提交即表示同意按隐私说明保存你的邮箱,只用于发送简报。

或用 RSS 订阅

AI 翻译、摘要和解读仅用于辅助判断;涉及重要决策时,请回到上方来源核实。