Oracle在北京时间4月15日发布了本年度第二次的Critical Patch Update Advisory,并在最新版VirtualBox(6.1.6)中修复了支付宝光年安全实验室@鹜望与@林以提交的USB XHCI模块的堆越界读写漏洞。这篇文章将对此漏洞进行分析。

VirtualBox USB XHCI漏洞模块介绍

VirtualBox是一款虚拟化软件,由于其开源属性,在个人用户中盛行。VirtualBox大部分模块是开源的,但是部分模块是以二进制插件包的形式发布,包括了,USB 2.0与 3.0虚拟设备,VirtualBox RDP协议模块,硬盘加密,NVMe以及用于 Intel的 PXE引导部分。CVE-2020-2905 漏洞存在于 VirtualBox二进制插件包中的 USB 3.0 XHCI模块。USB协议目前有三代接口标准,OHCI、UHCI都是 USB1.1的接口标准,而 EHCI是对应 USB2.0的接口标准,最新的 xHCI是 USB3.0的接口标准。在这里我们只需关注 XHCI协议的数据结构。

漏洞分析

漏洞简介

该漏洞存在于二进制插件包 VBoxEhciR3.so中,VBoxEhciR3.so包括了 Ehci以及 Xhci的实现。VirtualBox在 6.1版本之后将插件包中的二进制的符号都给 strip了,这里我们以 6.0.14带符号版本进行分析。复现环境中宿主机与虚拟机均为 Ubuntu,宿主机(Host)版本:

虚拟机(Guest):

漏洞成因

•该漏洞是个数组越界读写,存在于 xhciR3WriteEvent函数, iIntr 参数可以被攻击者控制。从IDA Pro的 Structures段可以得知 aInterrupters 数组只有8个元素(下图所示),但是iIntr的值却可以是0~1023。iIntr用来对 aInterrupters数组进行索引,但代码未对iIntr值进行校验,因此造成了堆溢出。

•逆向分析回溯调用流,可发现,xhciR3WriteEvent的调用路径是:

xhciR3WorkerLoop->xhciR3PostXferEvent->xhciR3WriteEvent。

•具体而言,当xhci模块正常启动时,它会调用xhciR3WorkerLoop函数。在该函数中,宿主机的xhci驱动会与虚拟机通过xhci命令进行通信。宿主机会等待xhci相应的物理寄存器的读写来进行响应。后面的文章我们将通过理清XHCI协议的相应数据结构来进行分析。

XHCI协议数据结构分析

•虚拟机通过读写宿主机的物理地址来与宿主机响应驱动进行通信。通过在虚拟机中执行cat/proc/iomem命令,我们可以得知宿主机相应的驱动与其映射出来的物理地址,如下我们可知,xhci驱动对应的物理地址范围是0xf1810000-0xf181ffff,0x10000个字节大小。

•而xhci驱动会将物理地址转换成xhci协议中的寄存器,当我们在虚拟机对物理地址进行读写时,其实就是对xhci协议的相应寄存器进行读写,读者可查阅XHC Ispecification[1]来对xhci协议进行深入了解。

•该漏洞的触发需要CRCR以及DCBAAP两个寄存器的配合:

CRCR寄存器

•CRCR(Command Ring Control)寄存器存储的内容用于对xhchi驱动进行控制,存储着dequeue pointer,指针指向trb ring,trb ring是个数组存储着trb结构体。对应到CRCR寄存器,存储的是Command TRBs。Command TRBs包含了17个commands用来对宿主机xhci驱动发起命令,而xhciR3WorkerLoop函数就是在轮询处理Command TRBs的command,包括了Enable Slot Command,Address Device Command,Stop Endpoint Command等commands,在specification 6.4.3节中定义。

•这17个commands中,我们需要关注Stop Endpoint Command,xhciR3WorkerLoop就是在处理Stop Endpoint Commahnd中触发了漏洞。我们就以Stop Endpoint Command TRB为例子来介绍TRB数据结构。一个TRB数据长128bits,在Stop Endpoint Command TRB中,前12个字节都是保留使用的,最后4个字节才有意义。其中TRB Type字段就是表明该TRB的类型,如果我们要发送Stop Endpoint Command,我们就要将该字段设为对应的值,Stop Endpoint Command对应值15。Slot ID最大值为32,即XHCI中最多拥有32个slot,slot的定义为:“Device Slot refers to the xHC interface associated with an individual USB device,e.g. the associated Device Context Base Address Array entry,a Doorbell Array register,and its Device Context.”

DCBAAP寄存器

• DCBAAP(Device Context Base Address Array Pointer Register)存储着指向Device Context Base Address Array的指针,该指针通过slot id来进行索引。而Device Context Base Address Array数组存储着Device Context Data Structure数据结构,Device Context Data Structure共有 32个入口,第一个入口是Slot Context Data Structure,其余的入口是Endpoint Context Data Structure。

漏洞具体触发方式

• 回到xhciR3WorkerLoop函数:

• 攻击者需要首先调用CR_ENABLE_SLOT命令来开启对应的slot,不然后续处理的时候遇到对应的slot没开启,程序会直接返回,在这里我们可以将所有32个 slot都开启。

• 在CR_STOP_ENDPOINT命令处理代码中,v134即slot id,通过v37 = xhciR3FetchDevCtxAddr(v2, v134);将对应slot id的Device Context Base Address Array的地址取出存到v37中,随后在pfnPCIPhysRead调用中读取 32字节到 icc_0变量中,由上文的XHCI协议数据结构分析中的Device Context Base Address Array结构分析可知,前32个字节应该是slot context数据结构。

• 随后,驱动会调用xhciR3PostXferEvent函数,该函数的第二个参数为 (unsigned __int16)(HIWORD(icc_0.resvd[0]) >> 6),即为被控制的icc_0中的数据。

参数(unsigned __int16)(HIWORD(icc_0.resvd[0]) >> 6)会传至uIntTgt变量,最后会传到xhciR3WriteEvent函数的iIntr参数,实现控制aInterrupters`中的index的值。

• 由上述分析可知icc_0中存储着slot context结构,我们需要分析出 (unsigned __int16)(HIWORD(icc_0.resvd[0]) >> 6) 对应的 slot context中的字段,以此来控制iIntr。查看 IDA Pro中 icc_0 结构体的定义,我们可以得知 (unsigned __int16)(HIWORD(icc_0.resvd[0]) >> 6)是Slot Context Data Structure中的Interrupter Target字段。该字段有10bits长,值范围是0~1023,意味着我们可以越界写1023个元素长。

• 总结而言,分析清楚这些数据结构对应的关系之后,我们就可以构造poc来触发该漏洞了,还有一些校验字段由于篇幅原因略去分析了,读者可以根据 specification以及 VirtualBox代码的实现来bypass对应的校验。

补丁分析

• VirtualBox在6.1.6版本修复了该漏洞,查看补丁,漏洞修复很简单,将iIntr值限定在0~7中。

可利用性分析

• 攻击者可以进行越界写,比如在函数xhciR3WriteEvent中变量v4是被攻击者控制的,下面代码会将v4 的值写入v7->errp中,v7即是从aInterrupters数组中取出的越界的地址。

• 攻击者通过内存布局,将代码中的关键数据结构布局到越界写的数据内容之后,可覆盖关键数据结构的值,从而造成虚拟机逃逸。

引用

[1]https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf

声明:本文来自支付宝安全实验室,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。