内容简介:通过精妙构造,VirtualBox虚拟机的主机进程可以访问VBoxDrv内核驱动程序。即便它以VM用户权限启动运行,同样也可以用于本地提权。在这篇博文中,我将结合 CVE-2018-3055 和 CVE-2018-3085 来破坏VirtualBox中启用了3D加速的虚拟机。这两个漏洞都已在VirtualBox最新的5.2.16版本中被修复。
通过精妙构造,VirtualBox虚拟机的主机进程可以访问VBoxDrv内核驱动程序。即便它以VM用户权限启动运行,同样也可以用于本地提权。
在这篇博文中,我将结合 CVE-2018-3055 和 CVE-2018-3085 来破坏VirtualBox中启用了3D加速的虚拟机。这两个漏洞都已在VirtualBox最新的5.2.16版本中被修复。
概述
3D加速功能作为 共享OpenGL
存在于代码库中,基于用来作分布式OpenGL渲染的Chromium库,不要与和它同名(但7年后才诞生)的Web浏览器相混淆
Chromium定义了一个描述OpenGL操作的网络协议,它可以传递给一个已存在的OpenGL实例
VirtualBox正在维护Chromium的一个分支,并通过 HGCM
(主机-客户端 通信管理器)隧道协议传输消息。HGCM本质上是一个非常简单的 客户端-主机 RPC协议,一旦连接到HGCM服务,客户端虚拟机就可以使用整数和缓冲区参数进行简单的远程调用,并在主机端处理它们。之后会返回一个状态代码,并且被调用者可能会更改参数,以便将数据传递回客户端虚拟机
值得注意的是,HGCM接口由 客户端虚拟机添加程序 暴露给非特权进程。如果没有安装 客户端虚拟机添加程序,则需要root权限才能安装 客户端虚拟机驱动程序 并且需要公开设备,以便攻击共享的OpenGL。
Chromium消息基础
存在不同类型的Chromium消息,它们表示为 CRMessage联合类型
typedef struct { CRMessageType type; unsigned int conn_id; } CRMessageHeader; typedef struct CRMessageOpcodes { CRMessageHeader header; unsigned int numOpcodes; } CRMessageOpcodes; typedef struct CRMessageRedirPtr { CRMessageHeader header; CRMessageHeader* pMessage; #ifdef VBOX_WITH_CRHGSMI CRVBOXHGSMI_CMDDATA CmdData; #endif } CRMessageRedirPtr; typedef union { CRMessageHeader header; CRMessageOpcodes opcodes; CRMessageRedirPtr redirptr; ... } CRMessage;
该类型存储在 header.type
字段中,我们重点关注 CR_MESSAGE_OPCODES
和 CR_MESSAGE_REDIR_PTR 消息
。 CR_MESSAGE_OPCODES消息
包含作为前缀的操作码数,紧接着的是描述实际Chromium操作码的字节数组,这些操作码以特殊方式编码。 例如,一条简单的消息可能如下所示:
uint32_t message[] = { CR_MESSAGE_OPCODES, // msg.header.type 0x41414141, // msg.header.conn_id 1, // msg.numOpcodes CR_EXTEND_OPCODE << 24 // 8-bit opcode indicating an extended opcode follows 0x42424242, // <padding, for whatever reason> CR_WRITEBACK_EXTEND_OPCODE // 32-bit extended opcode 0x43434343, // some extra payload data for this opcode 0x44444444, };
每个操作码都有一个关联的 解包器 和 调度程序,分别以 crUnpack
和 crServerDispatch
为前缀。 此特定操作码的解包器如下所示:
/* in cr_unpack.h */ extern CRNetworkPointer * writeback_ptr; // ... #define SET_WRITEBACK_PTR( offset ) do { CRDBGPTR_CHECKZ(writeback_ptr); crMemcpy( writeback_ptr, cr_unpackData + (offset), sizeof( *writeback_ptr ) ); } while (0); /* in unpack_writeback.c */ void crUnpackExtendWriteback(void) { /* This copies the unpack buffer's CRNetworkPointer to writeback_ptr */ SET_WRITEBACK_PTR( 8 ); cr_unpackDispatch.Writeback( NULL ); }
这告诉Chromium将偏移量为8处的Payload写回到响应缓冲区,上面例子中的字符串是“ ccccdddd
”。我不确定这对此功能的正常使用有什么必要性,但它为我们提供了一个“ echo
”语句来写回我们控制的数据,这对于利用漏洞肯定是有很大帮助的。
CVE-2018-3055
Chromium消息解析器中有几个位置,其中 SET_RETURN_PTR
和 SET_WRITEBACK_PTR
使用用户控制的偏移调用。比如 src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c
里的 crUnpackExtendAreProgramsResidentNV
:
void crUnpackExtendAreProgramsResidentNV(void) { GLsizei n = READ_DATA(8, GLsizei); const GLuint *programs = DATA_POINTER(12, const GLuint); SET_RETURN_PTR(12 + n * sizeof(GLuint)); SET_WRITEBACK_PTR(20 + n * sizeof(GLuint)); (void) cr_unpackDispatch.AreProgramsResidentNV(n, programs, NULL); }
我们在Chromium消息响应中的 return_ptr
和 writeback_ptr
处接收数据,并且能完全控制 n
。这意味着泄漏的数据可以位于消息缓冲区中的任意偏移量处,而无需进行边界检查。唯一的限制是 n
必须为非负数,否则我们将遇到其他整数溢出问题,在调用程序时崩溃。由于我们能够通过值 n
控制消息的分配大小和泄漏的偏移量,因此这是一个完美的用来公开 存储在堆上的指针 和 数据 的语句。
CVE-2018-3085
Chromium消息最终由 src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c
里的 crServerDispatchMessage
函数处理
static void crServerDispatchMessage(CRConnection *conn, CRMessage *msg, int cbMsg) { // ... if (msg->header.type == CR_MESSAGE_REDIR_PTR) { #ifdef VBOX_WITH_CRHGSMI // this is defined in prod builds pCmdData = &msg->redirptr.CmdData; #endif msg = (CRMessage *) msg->redirptr.pMessage; } CRASSERT(msg->header.type == CR_MESSAGE_OPCODES); msg_opcodes = (const CRMessageOpcodes *) msg; opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03; // handle opcodes here... #ifdef VBOX_WITH_CRHGSMI if (pCmdData) { int rc = VINF_SUCCESS; CRVBOXHGSMI_CMDDATA_ASSERT_CONSISTENT(pCmdData); if (CRVBOXHGSMI_CMDDATA_IS_SETWB(pCmdData)) { uint32_t cbWriteback = pCmdData->cbWriteback; rc = crVBoxServerInternalClientRead(conn->pClient, (uint8_t*)pCmdData->pWriteback, &cbWriteback); Assert(rc == VINF_SUCCESS || rc == VERR_BUFFER_OVERFLOW); *pCmdData->pcbWriteback = cbWriteback; } VBOXCRHGSMI_CMD_CHECK_COMPLETE(pCmdData, rc); } #endif }
很明显,如果 msg 完全由 客户端 控制,将能够以各种方式中断。尤其,客户端虚拟机可以将消息类型设置为 CR_MESSAGE_REDIR_PTR
,并设置 msg-> redirpt
用来将其指向伪造的 CR_MESSAGE_OPCODES
消息。如果伪造的消息产生响应,它将被写入 pCmdData-> pWriteback
,这也是攻击者能够控制的,因为它是从 msg-> redirptr
获取过来的。现在我们已经知道可以使用 CR_WRITEBACK_EXTEND_OPCODE消息
来控制8个字节的响应,如果我们可以注入 CR_MESSAGE_REDIR_PTR消息
,问题将仍然存在。
如果通过HGCM访问Chromium子系统 src/VBox/GuestHost/OpenGL/util/vboxhgcm.c
里的 _crVBoxHGCMReceiveMessage
函数,将负责从缓冲区读取消息并将其放入Chromium处理队列中:
static void _crVBoxHGCMReceiveMessage(CRConnection *conn) { // ... if (conn->allow_redir_ptr) { // ... // [[ 1 ]] hgcm_buffer = (CRVBOXHGCMBUFFER *) _crVBoxHGCMAlloc( conn ) - 1; hgcm_buffer->len = sizeof(CRMessageRedirPtr); msg = (CRMessage *) (hgcm_buffer + 1); msg->header.type = CR_MESSAGE_REDIR_PTR; msg->redirptr.pMessage = (CRMessageHeader*) (conn->pBuffer); msg->header.conn_id = msg->redirptr.pMessage->conn_id; // ... cached_type = msg->redirptr.pMessage->type; // ... } else { /* we should NEVER have redir_ptr disabled with HGSMI command now */ CRASSERT(!conn->CmdData.pvCmd); if ( len <= conn->buffer_size ) { // [[ 2 ]] /* put in pre-allocated buffer */ hgcm_buffer = (CRVBOXHGCMBUFFER *) _crVBoxHGCMAlloc( conn ) - 1; } else { // [[ 3 ]] /* allocate new buffer, * not using pool here as it's most likely one time transfer of huge texture */ hgcm_buffer = (CRVBOXHGCMBUFFER *) crAlloc( sizeof(CRVBOXHGCMBUFFER) + len ); hgcm_buffer->magic = CR_VBOXHGCM_BUFFER_MAGIC; hgcm_buffer->kind = CR_VBOXHGCM_MEMORY_BIG; hgcm_buffer->allocated = sizeof(CRVBOXHGCMBUFFER) + len; } hgcm_buffer->len = len; _crVBoxHGCMReadBytes(conn, hgcm_buffer + 1, len); msg = (CRMessage *) (hgcm_buffer + 1); cached_type = msg->header.type; } // ... // [[ 4 ]] crNetDispatchMessage( g_crvboxhgcm.recv_list, conn, msg, len ); // [[ 5 ]] /* CR_MESSAGE_OPCODES is freed in crserverlib/server_stream.c with crNetFree. * OOB messages are the programmer's problem. -- Humper 12/17/01 */ if (cached_type != CR_MESSAGE_OPCODES && cached_type != CR_MESSAGE_OOB && cached_type != CR_MESSAGE_GATHER) { _crVBoxHGCMFree(conn, msg); } }
我们将看到两种不同的情况:
如果 conn-> allow_redir_ptr
为 true,则分配 CR_MESSAGE_REDIR_PTR消息
并指向客户端虚拟机所提供的消息。如果不是这种情况,则将 客户端虚拟机消息
直接放入消息队列中。
触发漏洞
如果 allow_redir_ptr
永远为 true,那么由于 _crVBoxHGCMAlloc
的工作方式, use-after-free
将无法被利用。 那这个标志是什么意思呢? cr_net.h
中的注释给出了一个线索:
/* Used on host side to indicate that we are not allowed to store above pointers for later use * in crVBoxHGCMReceiveMessage. As those messages are going to be processed after the corresponding * HGCM call is finished and memory is freed. So we have to store a copy. * This happens when message processing for client associated with this connection * is blocked by another client, which has send us glBegin call and we're waiting to receive glEnd. */ uint8_t allow_redir_ptr;
由于Chromium必须能够同时处理多个连接,即VirtualBox的多个HGCM连接,它需要复用来自不同客户端所有传入的OpenGL命令。如果一个客户端发送了 glBegin
,它将无法处理来自其他客户端的命令,直到接受了相应的 glEnd
。
虽然情况确实如此,但对于其他客户端, allow_redir_ptr
仍然为 false。在 src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c
的 crVBoxServerInternalClientWriteRead
函数中可见端疑:
if ( #ifdef VBOX_WITH_CRHGSMI !CRVBOXHGSMI_CMDDATA_IS_SET(&pClient->conn->CmdData) && #endif cr_server.run_queue->client != pClient && crServerClientInBeginEnd(cr_server.run_queue->client)) { crDebug("crServer: client %d blocked, allow_redir_ptr = 0", pClient->conn->u32ClientID); pClient->conn->allow_redir_ptr = 0; } else { pClient->conn->allow_redir_ptr = 1; }
因此,要触发 allow_redir_ptr == 0
分支,我们只需要在一个客户端发出 glBegin
,然后在另一个客户端发送伪造的消息,它将被放入消息队列中而不进行检查。在发送了 glEnd
后,才会被处理。所以一下是第一个攻击计划:
glBegin CR_MESSAGE_REDIR_PTR glEnd
然而这并没有什么卵用
我们必须继续处理,因为以下事件并不会自动完成:由于 路径[[5]]
中不合适的空闲时机,我们在步骤2中发送的消息在被处理之前释放掉了。如果我们选择通过 路径[[2]]
在步骤2中分配消息,那么来自步骤3的消息又将覆盖它并将被处理两次。如果我们通过 路径[[3]]
分配它,那么(至少在 Linux 和Windows上)在 free(释放)之后它所包含的一些堆元数据将会是些无效数据。
所以,替代方案如下
glBegin
2 . 在客户端B中发送一个大的伪造 CR_MESSAGE_REDIR_PTR
,它将触发 路径[[3]]
(操作系统提供的 malloc函数)
3 .通过调用具有相同大小和内容的 HGCM
来写入一些缓存,希望它们占用被释放掉的消息缓冲区
glEnd
最终,来自 步骤3 的消息将在处理之前重用 步骤2 中的消息空间,且最终结果如我们所期待的那样:完全控制传递给 crServerDispatchMessage
的消息,并实现 write-what-where
语句
与之前的信息泄漏配合,这可以变成更灵活和可重复的写语句,并最终实现 任意读/写
。
以上所述就是小编给大家介绍的《结合CVE-2018-3055与CVE-2018-3085攻击VirtualBox 3D加速》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 简单的shell脚本结合awk实现防止对web服务的dos攻击
- 代理模式——结合SpringAOP讲解
- 如何结合 Scrum 和 Kanban
- NServiceBus 结合 RabbitMQ 使用教程
- ActiveMQ结合Spring收发消息
- quicklink学习以及结合React
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Cascading Style Sheets 2.0 Programmer's Reference
Eric A. Meyer / McGraw-Hill Osborne Media / 2001-03-20 / USD 19.99
The most authoritative quick reference available for CSS programmers. This handy resource gives you programming essentials at your fingertips, including all the new tags and features in CSS 2.0. You'l......一起来看看 《Cascading Style Sheets 2.0 Programmer's Reference》 这本书的介绍吧!