原文:https://www.voidsecurity.in/2018/08/from-compiler-optimization-to-code.html
目前,Oracle公司已经修复了本人在2010年4月提交的VirtualBox某些高危漏洞。CVE-2018-2844漏洞是一个有趣的Double Fetch型漏洞,该漏洞存在于 VirtualBox 的核心图形框架的VirtualBox视频加速功能(VBVA)中,影响Linux宿主机操作系统。VBVA功能是建立在VirtualBox宿主机-客户机共享内存接口(HGSMI)的基础之上的,而HGSMI则是通过视频RAM缓冲区实现的共享内存。其中,该VRAM缓冲区的物理地址为0xE0000000。
sudo lspci -vvv
00:02.0 VGA compatible controller: InnoTek Systemberatung GmbH VirtualBox Graphics Adapter (prog-if 00 [VGA controller])
...
Interrupt: pin A routed to IRQ 10
Region 0: Memory at e0000000 (32-bit, prefetchable) [size=16M]
Expansion ROM at [disabled]
Kernel modules: vboxvideo
客户机将使用如下所示的HGSMI来设置命令缓冲区,并将VRAM中的偏移量写入IO端口VGA_PORT_HGSMI_GUEST(0x3d0)中,以此来通知宿主机。
HGSMIBUFFERHEADER header;
uint8_t data[header.u32BufferSize];
HGSMIBUFFERTAIL tail;
该漏洞正好位于处理从客户机传递给宿主机的视频DMA(VDMA)命令的代码中。VDMA命令处理函数vboxVDMACmdExec()会根据VDMA命令的类型来调度相应的函数。实际上,这个调度过程是利用switch case语句实现的。
static int
vboxVDMACmdExec(PVBOXVDMAHOST pVdma, const uint8_t *pvBuffer, uint32_t cbBuffer)
{
/* pvBuffer is shared memory in VRAM */
PVBOXVDMACMD pCmd = (PVBOXVDMACMD)pvBuffer;
switch (pCmd->enmType) {
case VBOXVDMACMD_TYPE_CHROMIUM_CMD: {
...
}
case VBOXVDMACMD_TYPE_DMA_PRESENT_BLT: {
...
}
case VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER: {
...
}
case VBOXVDMACMD_TYPE_DMA_NOP: {
...
}
case VBOXVDMACMD_TYPE_CHILD_STATUS_IRQ: {
...
}
default: {
...
}
}
}
编译时,编译器将其中的条件分支优化为跳转表。经过优化后,代码将会变为:
; first fetch happens for cmp
.text:00000000000B957A cmp dword ptr [r12], 0Ah ; switch 11 cases
.text:00000000000B957F ja VBOXVDMACMD_TYPE_DEFAULT ; jumptable 00000000000B9597 default case
; second fetch again for pCmd->enmType from shared memory
.text:00000000000B9585 mov eax, [r12]
.text:00000000000B9589 lea rbx, vboxVDMACmdExec_JMPS
.text:00000000000B9590 movsxd rax, dword ptr [rbx+rax*4]
.text:00000000000B9594 add rax, rbx
.text:00000000000B9597 jmp rax ; switch jump
.rodata:0000000000185538 vboxVDMACmdExec_JMPS dd offset VBOXVDMACMD_TYPE_DEFAULT - 185538h
.rodata:0000000000185538 ; DATA XREF: vboxVDMACommand+1D9o
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DMA_PRESENT_BLT - 185538h ; jump table for switch statement
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DEFAULT - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DEFAULT - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DEFAULT - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DEFAULT - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DMA_NOP - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DMA_NOP - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DEFAULT - 185538h
.rodata:0000000000185538 dd offset VBOXVDMACMD_TYPE_DMA_NOP - 185538h
.rodata:0000000000185564 align 20h
问题已经很明显了,这是一个TOCTOU错误。由于变量未标记为volatile,因此,GCC的优化处理将导致共享VRAM内存的Double Fetch型漏洞。需要注意的是,由于Windows和OSX平台上的VirtualBox中没有进行这种优化处理,因此,该漏洞只影响Linux主机。
请注意,这类漏洞并不是现在才发现的。我们的研究人员发表的Xenpwn - Breaking Paravirtualized Devices报告中,就指出Xen也存在类似问题。
漏洞利用
虽然这个竞争条件的时间窗口非常小,但是对于拥有多个vCPU的客户机来说,还是可以进行“稳定的”利用的。
RAX 0xdeadbeef
RBX 0x7fff8abf2538 ◂— rol byte ptr [rdx - 0xd], 1
RCX 0x7fff9c508ac0 —▸ 0x7ffff7e30000 ◂— 0x5
RDX 0xe7b
RDI 0xeeb
RSI 0x7fffdc022000 ◂— xor byte ptr [rax], al /* 0xffe40030; '0' */
R8 0x7fff89d20000 ◂— jmp 0x7fff89d20010 /* 0xb020000000eeb */
R9 0x7fff8ab06040 ◂— push rbp
R10 0x7fff9c50ad48 ◂— 0x1
R11 0x7fff9c508d48 ◂— 0x0
R12 0x7fff89d20078 ◂— 0xa /* '\n' */
R13 0xf3b
R14 0x7fff9c50d0e0 —▸ 0x7fff9c508ac0 —▸ 0x7ffff7e30000 ◂— 0x5
R15 0x7fff89d20030 ◂— 0xffffffdc0f3b0eeb
RBP 0x7fffba44dc40 —▸ 0x7fffba44dca0 —▸ 0x7fffba44dce0 —▸ 0x7fffba44dd00 —▸ 0x7fffba44dd50 ◂— ...
RSP 0x7fffba44db80 —▸ 0x7fffba44dbb0 —▸ 0x7fff9c508ac0 —▸ 0x7ffff7e30000 ◂— 0x5
RIP 0x7fff8ab26590 ◂— movsxd rax, dword ptr [rbx + rax*4]
► 0x7fff8ab26590 movsxd rax, dword ptr [rbx + rax*4]
0x7fff8ab26594 add rax, rbx
0x7fff8ab26597 jmp rax
其中,RAX是由客户机控制的。并且,R8、R12和R15存放的是指向崩溃期间HGSMI缓冲区内的相关偏移量的指针。由于跳转表使用相对寻址方式,因此,无法直接访问指针。我的初步计划是找到一个这样的功能,可以将受控的值从客户机写入VBoxDD.so中,然后将其用作伪跳转表。遗憾的是,我没有找到符合要求的功能。
下一个选项是通过可用于伪跳转表的值,直接跳转到具有RWX权限的VRAM缓冲区。
// VRAM buffer
0x7fff88d21000 0x7fff89d21000 rwxp 1000000 0
// VBoxDD.so
0x7fff8aa6d000 0x7fff8adff000 r-xp 392000 0 /usr/lib/virtualbox/VBoxDD.so
0x7fff8adff000 0x7fff8afff000 ---p 200000 392000 /usr/lib/virtualbox/VBoxDD.so
0x7fff8afff000 0x7fff8b010000 r--p 11000 392000 /usr/lib/virtualbox/VBoxDD.so
0x7fff8b010000 0x7fff8b018000 rw-p 8000 3a3000 /usr/lib/virtualbox/VBoxDD.so
接下来,我们需要在VBoxDD.so(用作伪跳转表)中寻找一个在相对地址计算期间指向16MB共享VRAM缓冲区的值。对于概念验证漏洞利用代码来说,可以使用NOP填充整个VRAM,并将shellcode放置在映射的最后内存页中。由于该跳转指令是相对寻址的,因此不需要绕过ASLR。
在客户机中,将vboxvideo添加到/etc/modprobe.d/blacklist.conf。在vboxvideo.ko驱动程序中,建立一个自定义分配器来管理VRAM内存和HGSMI客户端的实现。将vboxvideo列入黑名单,可以减少VRAM上的活动,从而保证有效荷载的完整性。该漏洞利用代码的测试环境为:Ubuntu Server作为客户机,Ubuntu Desktop作为宿主机,其上运行VirtualBox 5.2.6.r120293。
大家可以在virtualbox-cve-2018-2844找到具有进程延续和网络连接功能的POC代码。
相关的演示视频,读者可以从这里观看。
参考文献:
[1] Xenpwn - Breaking Paravirtualized Devices by Felix Wilhelm
[2] SSD Advisory – Oracle VirtualBox Multiple Guest to Host Escape Vulnerabilities by Niklas Baumstark
[3] VM escape - QEMU Case Study by Mehdi Talbi & Paul Fariello
[4] Xen Security Advisory CVE-2015-8550 / XSA-155
[5] Oracle Critical Patch Update Advisory - April 2018