Parallels Desktop Guest 虚拟机逃逸漏洞的分析
2021-12-13 12:55:00 Author: www.4hou.com(查看原文) 阅读量:27 收藏

parallels desktop 是目前苹果os x系统上最强大的一款虚拟机软件,已经针对Windows 10和 macOS Sierra进行优化,随时可供macOS Sierra使用,可以在苹果的macOS系统上同时运行一个或多个Windows或Linux系统,并能随意在它们之间切换,让你可以运行各个平台上几乎所有的应用程序和游戏。当 Parallels Desktop 被宣布为Zero Day Initiative 的 Pwn2Own Vancouver 2021 虚拟化类别的新目标时。

这篇文章详细介绍我在 Parallels Desktop 中找到并成功利用漏洞的方法。为了取得成功,我需要利用虚拟机hypervisor代码中的一个未知漏洞,从在用户虚拟机中运行代码转向在主机上执行代码。这一过程涉及到几个步骤,但第一步才刚刚开始。然后我需要找到一个可利用的漏洞并充分了解系统,以便利用它来让代码在主机上运行。对于 Pwn2Own,主机需要是运行 Big Sur 的完全更新的 Mac OSX。这后来证明是有问题的,因为最初我只有一台无法运行 Big Sur 的旧 iMac。

第一步

我首先安装了最新版本的 Parallels Desktop 并构建了一个 Ubuntu 虚拟机 (VM)。我发现可以在主机上的 parallels.log 文件中找到 VM 的日志。然后我开始查看一些提供的实用程序,并了解到我可以使用 prlsrvctl 程序打开详细日志记录。

在查看了日志后,我发现系统正在从某处加载hypervisor代码。此时,我不知道这段代码位于磁盘上的哪个位置。 Parallels还有一个名为prl_vm_app的主机用户模式组件。Reno Robert早期的研究通过模糊VGA设备的端口I/O地址发现了prl_vm_app中的一个漏洞。以前在其他hypervisor(VMware Workstation和VirtualBox)中的Pwn2Own漏洞也针对虚拟硬件组件。

第二步

我写了一个最简单的端口 I/O 模糊器。第一个模糊器只是将随机数据写入用户中的随机 I/O 端口地址。值得注意的是,仅对几个虚拟硬件设备(通过读取 /proc/ioports 文件并运行 lspci 命令进行识别)进行模糊测试后,虚拟机hypervisor崩溃了!这些特定的崩溃不在 prl_vm_app 用户模式进程中。相反,它们位于高度特权的hypervisor代码中!Parallels 帮助生成了包含详细日志和数据转储的崩溃报告。从日志中可以明显看出,崩溃的原因是hypervisor中的 ASSERT。这种崩溃对 Pwn2Own 不太可能有用,因为ASSERT是对导致执行在内存损坏或其他可利用条件发生之前停止的错误条件的测试。但是,日志提供了回溯(带有函数名称!)并在ASSERT发生的地方注册内容:

1.png

Parallels以Mach-O二进制文件的形式提供了hypervisor代码的内存转储!这对前面的逆向工程非常有帮助。

第三步

我编写了另一个简单的模糊器,它对内存映射 I/O (MMIO) 进行了模糊测试。没什么特别的,只是随机字节、字和双字到由 /proc/iomem 文件标识的随机内存地址。虚拟机再次快速崩溃。出现更多的ASSERT。此时,来自两个 fuzzer 的 ASSERT 足以怀疑 fuzzer 方法是否可行。 fuzzer 只是继续满足这些非常浅的 ASSERT 条件,然后程序退出。似乎如果我要继续模糊测试,为此我要获取易受攻击的代码,而不会影响到 ASSERT。

然而,在浏览 ASSERT 时,我意识到其中一个与其他的不同,而是hypervisor中的页面错误。页面错误处理程序打印调试信息然后ASSERT!这可能很有趣,因为它可能意味着某种内存损坏漏洞导致读取或写入未映射的内存,类似于用户空间程序中的分段错误。我迅速对其余的崩溃进行了分类,看看是否还有其他有趣的事情,然后在崩溃的 MMIO 地址上重新运行模糊器。它一直是页面错误,但在不同的地方有不同的寄存器内容。

第四步

对 virtio VGA 设备进行模糊测试时发生崩溃,特别是在 CommonCfg 设置 MMIO 区域中:

2.png

根据日志文件中的以下回溯,崩溃似乎发生在 PciVirtIOWriteMM 函数中:

3.png

在阅读 virtio 规范、将模糊的 MMIO 范围一分为二并对PciVirtIOWriteMM函数进行逆向之后,这个漏洞很快就显现出来了。该漏洞存在于virtio公共配置的处理中。代码将客户提供的数据(EBX)存储在“驱动程序特性选择”寄存器(一个全局变量)中,没有任何边界检查。

存储的值被用作索引,在“驱动程序特性集”寄存器中写入客户控制值。

在跟踪了由用户控制的全局变量的所有使用之后,在 PciVirtIOReadMM 函数中找到了相应的相对读取原语。

然后将读入 R8D 的值返回给用户。

这是一个非常强大的开发原语,数据部分的相对读写应该是可取的!

第五步

接下来,我需要使用相对读/写原语来控制指令指针,然后在hypervisor中执行代码。我使用相对读/写原语在未使用的地址范围内安装端口 I/O 处理程序。鉴于我可以轻松地从用户触发处理程序的使用,这似乎是控制指令指针的好方法。

当端口I/O(不管是IN还是out)被执行时,__bss中的struct io_port_handler条目数组通过地址索引来找到合适的处理程序。io_port_handler结构可以通过查看AssignInPortFunc和AssignOutPortFunc进行反向工程,看起来如下所示:

7.png

然后执行处理程序,并将适当的数据指针作为参数传递。我找到了一个未使用的端口 I/O 地址范围,并在数组中写入了一个指针,该指针指向安装在 __bss 部分末尾数据洞穴中的假结构 io_port_handler。在适当的地址上发出 IN 触发了我安装在数据洞中的处理程序的执行,并获得了对指令指针的控制。

一旦我控制了指令指针,在hypervisor中运行 shellcode 就很容易了,因为没有内存保护来防止从 __data 或 __bss 部分执行代码。我在与虚假结构 io_port_handler 相同的数据洞中编写了 shellcode,然后将 in_handler 和 out_handler` 指向了 shellcode 的位置。适当地址上的后续IN或OUT将执行shellcode。

第六步

此时,我在 Parallels hypervisor中运行了 shellcode。对于 Pwn2Own,我需要转向主机并证明我完全控制了系统。这个过程很简单,但有些痛苦和费力:

1.将物理内存页映射到hypervisor中,使其可读写。经过大量逆向工程后,我在hypervisor中找到了一组为我执行此操作的函数。

2.扫描主机中的目标页面。理想情况下,这将是一个在 prl_vm_app 中带有代码的页面,可以很容易地通过用户被触发。

3.如果找到目标页面,用弹出 calc 的 shellcode 覆盖它。否则,重新返回步骤 1。

Big Sur的开发

到目前为止,我一直在使用旧的 iMac。不幸的是,旧硬件不支持 Pwn2Own 需要的Big Sur。经过实践,在 Big Sur 上,苹果引入了一个hypervisor API,它改变了hypervisor与主机内核交互的方式。

Parallels 不再控制新 API 中的hypervisor,因此我需要确定易受攻击的代码是否仍然存在。为此,我需要找到它。事实证明,大部分虚拟设备代码都被抽象为一个名为 libMonitor.dylib 的主机用户模式库。经过一些紧张的逆向工程,我确定漏洞仍然存在。但是,我需要完全更改漏洞利用以解决用户模式进程中存在的所有漏洞利用缓解措施。我的计划是先绕过ASLR,把相对读/写原语变成绝对读/写原语,然后想办法让任意代码执行。

在相对读取基地址的偏移量 6(24 字节)处读取会为 __common 部分中的另一个对象提供地址。这使我们能够定位 libMonitor.dyld 的基地址并绕过 ASLR。

创建任意读/写原语

我再次操作 __bss 中的端口 I/O 地址处理程序数组以升级到任意读写原语。当执行端口 I/O 时,io_port_handler 结构中的指针作为参数传递给处理程序函数。通过控制这些指针(在 R8 中传递),我们可以使用现有的“AhciIdpIndexInPortFunc”获得绝对读取原语。

使用AhciIdpIndexOutPortFunc绝对写原语。

执行原语

此时我就有了ASLR绕过,绝对读写原语,以及控制指令指针的能力。为了演示 Pwn2Own 的执行控制,我选择调用“echo \”pwned!\” | open -f”。通过读取 __la_symbol_ptr::_gettimeofday (libMonitor.dylib + 0x109148).找到系统地址。这将在动态链接器缓存中提供一个地址。在与此相距甚远的地方可以找到系统函数。找到一种好方法,将绝对的写入和执行控制转换为使用受控字符串调用“系统”的能力,需要大量的反向工程。我在SMBus虚拟设备的实现中发现了一个很好的小工具,可以从内存写入转向调用我们选择的函数,并使用指向我们控制的数据的指针作为第一个参数。该小工具位于 smb_write (libMonitor.dylib + 0x30c30) 中。在将pcVar6指向系统并在pbVar2上写入命令字符串之后,所需要做的就是触发smb_write函数的执行。成功触发将导致texttedit显示字符串“pwned!”:

11.png

然后漏洞利用被清除并继续正常执行。

本文翻译自:https://trenchant.io/pwn2own-2021-parallels-desktop-guest-to-host-escape/如若转载,请注明原文地址


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