Parallels Desktop虚拟机Guest/Host ToolGate通信协议的漏洞分析
2021-10-26 12:55:00 Author: www.4hou.com(查看原文) 阅读量:53 收藏

导语:Parallels Desktop 被称为 macOS 上最强大的虚拟机软件。可以在 Mac 下同时模拟运行 Win、Linux、Android 等多种操作系统及软件而不必重启电脑,并能在不同系统间随意切换。

Parallels Desktop使用名为“Parallels ToolGate”的半虚拟PCI设备,用于Guest操作系统和主机操作系统之间的通信。该设备在Parallels Guest中由Vendor ID 0x1AB8和device ID 0x4000标识。

Parallels Desktop 被称为 macOS 上最强大的虚拟机软件。可以在 Mac 下同时模拟运行 Win、Linux、Android 等多种操作系统及软件而不必重启电脑,并能在不同系统间随意切换。

最新版的 Parallels Desktop 17 (PD17) 完美支持最新的 macOS Monterey 和 Windows 11、Win10,并充分优化!可不重启直接在 Mac 系统上运行 Windows 应用软件、游戏、使用诸如 Office 办公软件、IE 浏览器、VisualStudio、AutoCAD 等工具。新版 PD17 支持 DirectX 11,大幅提升了启动速度以及 3D 游戏图形性能!

作为Parallels Tools的一部分提供的Guest驱动程序和主机虚拟设备使用ToolGate消息协议进行通信。为了提供一个摘要,Guest驱动程序准备一个消息,并将消息的物理地址写到TG_PORT_SUBMIT [PORT IO address +0x8]。然后,主机映射Guest提供的物理地址,解析消息,并将控制传递给各自的请求处理程序以进行进一步处理。Parallels中ZDI程序接收到的许多错误都在这些请求处理程序中。

ToolGate接口和协议格式

ToolGate请求格式是一种可变大小的结构,可以跨多个页面。Guest通过将TG_PAGED_REQUEST结构的物理地址写到虚拟设备的IO端口,将数据以内联字节或指向分页缓冲区的指针的形式发送给主机。

1.png

Guest内存中可变大小的TG_PAGED_REQUEST结构

2.png

TG_PAGED_REQUEST中可变大小的TG_PAGED_BUFFER结构

然后,主机映射物理地址所指向的页面,根据Guest提供的信息准备主机缓冲区,然后调用请求处理程序。请求处理程序可以使用内联字节或分页缓冲区,或同时使用内联字节或分页缓冲区来读写数据。这些数据缓冲区使用一组函数来访问,大致定义如下:

3.png

针对Pwn2Own 2021的STARLabs提交(ZDI-21-937)是一个不受控制的内存分配漏洞,其中Guest提供的大小值在堆栈中分配。如果Guest端提供的大小大于栈的总大小,则可以将堆栈指针(RSP)移到进程内存的其他区域(例如,另一个线程的堆栈区域)。

4.png

正常的堆栈操作(左)和由于大量分配而导致的堆栈跳转(右)

当处理TG_REQUEST_INVSHARING (0x8420)时,Parallels Desktop不验证Guest端作为TG_PAGED_BUFFER的一部分提供的ByteCount值。当在堆栈中分配时,这个不受信任的大小值可用于将处理ToolGate请求的线程的堆栈顶部移动到另一个受害线程,以覆盖其内容。有一个大小为 4KB 的保护页,但是可以跳过这个小的分配而不访问它。早在2017年,Qualys就这个漏洞发表了一篇名为《堆栈冲突》(The Stack Clash)的详细论文,其中就讲解了各种缓解措施,以防止这种保护页面跳转。

下面是来自Parallels Desktop 16.1.3的易受攻击的代码部分。在调用TG_ReadBuffer()期间,另一个线程的堆栈内存可以被Guest控制的值覆盖。

5.png

TG_REQUEST_INVSHARING处理中的漏洞

向后兼容性和编译程序缓解

这里最有趣的问题是 Apple Clang 中的堆栈冲突编译器缓解发生了什么?当像 alloca(value) 或 char buffer[value] 一样在堆栈中进行可变大小分配时,Apple Clang 编译器使用 ___chkstk_darwin() 检测分配以验证分配请求的大小。 ___chkstk_darwin() 本质上在堆栈中分配一个 PAGE_SIZE 的内存,然后,对于每个分配的页面,如果可以访问,则探测新的堆栈顶部。如果到达保护页,探测步骤将失败,导致安全崩溃。不再可能将堆栈指针移动到任意位置。

6.png

具有堆栈冲突缓解的示例代码

很明显,Parallels Desktop 没有启用此缓解措施,因为在可变大小堆栈分配期间没有调用 ___chkstk_darwin(),这就不得不让人有以下疑问:

Parallels是否使用-fno-stack-check编译程序标识禁用了缓解?

他们是否使用了禁用缓解的旧构建配置?

Mac OS otool可以用来获取大量关于构建环境的信息。具体来说,LC_VERSION_MIN_MACOSX可以提供有关所支持的macOS版本的信息。以下是otool -l prl_vm_app的输出信息:

7.png

prl_vm_app的LC_VERSION_MIN_MACOSX信息

使用的 SDK 是 10.15 (Catalina),这是一个很新的版本。此外,Parallels 还将支持的最低 macOS 版本设置为 10.13,使其与 High Sierra 兼容。这与他们的知识库文章中提供的兼容性信息一致。但是,10.13的向后兼容性是否禁用了编译程序缓解?下面是使用和不使用-mmacosx-version-min=10.13编译的示例代码的比较:

8.png

向后兼容 10.13 禁用 ___chkstk_darwin()(右)

目前尚不清楚 Parallels 是否使用 -fno-stack-check 明显禁用了 ___chkstk_darwin(),但设置 -mmacosx-version-min=10.13 具有相同的效果并默默地失去缓解效果。 -mmacosx-version-min=10.14 (Mojave) 也观察到相同的行为。有趣的是,GCC 已经内联了堆栈探测检查,而不是依赖于外部库函数。这可能是由于外部依赖性,因为在 Apple Clang 中使用向后兼容标识(macosx-version-min、target 等)进行编译最终失去了缓解措施。

也就是说,Mojave(10.14.6)在调用__chkstk_darwin()运行可执行文件时没有给出任何符号错误。你可以在High Sierra(10.13.6)中找到许多这样的漏洞。值得注意的是,在较老的编译程序中(例如Mojave上的Apple LLVM版本10.0.1),除非明确提供-fstack-check标识,否则默认情况下不会启用堆栈冲突缓解。因此,最近的编译程序在使用macosx-version-min编译任何低于10.15的版本时,似乎完全放弃了这种缓解。这可以通过同时提供-fstack-check和-mmacosx-version-min标识来解决。但是,High Sierra 的兼容性值得怀疑。重点是,即使在最新版本的 Mac OS 上,单独使用 macosx-version-min 也可以使该漏洞可利用。

9.png

Apple Clang 调用 ___chkstk_darwin(左)与 GCC 缓解内联(右)

使用 Binary Ninja 进行检测

Binary Ninja官方版是一款简单易用的编译工具,Binary Ninja官方版提供了许多的方式来修改二进制文件,大致可以分为低级和高级模式两种,其中低级模式主要就是原始码的十六进制编辑和汇编模式,高级模式可以使用内置的C编译器直接书写C代码来进行操作!

下一个问题是,Parallels Desktop 中是否还有其他类似的漏洞,我们如何使这个过程自动化?堆栈帧的大小通常在编译时就知道了。此外,还可以跟踪任何移动堆栈指针的操作。Binary Ninja 具有静态数据流功能,可以跟踪函数中任何一点的堆栈帧偏移量。然而,当堆栈中存在可变大小分配时,无法静态确定堆栈偏移量。这个确切的属性可以用来查找漏洞的变体。

10.png

已知堆栈偏移量(左)与 alloca() 后的未确定值(右)

考虑上面的低级别IL中的索引88,其中加载了RSP寄存器。

11.png

这样,新的栈顶使用Guest提供的大小计算并加载到 RSP 中,Binary Ninja 提供 get_possible_reg_values() 和 get_possible_reg_values_after() python API 来获取寄存器的静态确定值。寄存器值还与类型信息 (RegisterValueType) 相关联。下面是RSP中加载操作前后的栈帧偏移值:

12.png

RSP 始终与 StackFrameOffset RegisterValueType 相关联,但是,当 RSP 值未知时,它会被标记为 UndeterminedValue。使用这个值类型信息,搜索对 TG_ReadBuffer() 的所有引用,其中 RSP 值未确定。如果在调用 TG_ReadBuffer() 之前未确定 RSP,则推断在调用之前在堆栈中进行了可变大小分配。

13.png

上面的查询产生了 3 个结果:一个Pwn2Own 提交和另外两个以前未知的漏洞。

14.png

使用 Binary Ninja 发现的 Pwn2Own 漏洞及其变种

总结

研究表明,在某些情况下,向后兼容的编译可能会自动删除当前已有的缓解措施,从而使整个漏洞类都可以被利用,也许供应商应该考虑在这种情况下发布单独的二进制文件?

另外,本文还介绍了Binary Ninja的静态数据流功能和 Python API 如何在自动化漏洞查找任务中发挥作用。

本文翻译自:https://www.zerodayinitiative.com/blog/2021/9/9/analysis-of-a-parallels-desktop-stack-clash-vulnerability-and-variant-hunting-using-binary-ninja如若转载,请注明原文地址


文章来源: https://www.4hou.com/posts/PWmA
如有侵权请联系:admin#unsafe.sh