漏洞描述

在VMware x64 Guest OS中, 利用其模拟指令的缺陷可导致以ring 0权限执行任意代码。

漏洞分析

下面的伪汇编片断提供了一个 x64体系下中断处理函数的典型实现,结合Vmware的指令模拟缺陷可产生安全漏洞:


ISR_Entry_Point:

    ; For a long-mode (64-bit) ISR, RSP points to the following QWORDs:
    ;
    ;   [<error code>]
    ;   <return RIP> 
    ;   <return CS> 
    ;   <return RFLAGS>
    ;   [<return RSP> 
    ;   <return SS>]
    ;
    ; 一个典型的中断服务例程 首先会创建一个标准的陷阱帧。
    ; The first act of typical ISR prologue code is to build a standard
    ; "trap frame" on the stack -- saving registers, etc.

     ...                                        ; GS -> user or kernel

    ; If the CPL at the time of the fault (recorded in the two least
    ; significant bits of <return CS>) was zero, then the fault occurred
    ; in kernel mode; some OSes then assume that kernel GS is already
    ; active, and will therefore skip the SWAPGS instruction.

    ; 这里测试发生异常时的cpl,如果为0,则不会进行GS切换
    TEST    [return CS], (1, 2, or 3)           ; GS -> user or kernel
    JZ      Skip_Swap                           ; GS -> user or kernel

    ; If the previous mode was user mode, then it is assumed that the
    ; user GS base address is loaded, so SWAPGS will exchange the
    ; value in the KernelGSbase MSR (MSR C000_0102h) with the base
    ; address in the GS shadow descriptor, in effect switching from
    ; user GS to kernel GS.

    ; 如果发生中断时的cpl不等于0,即位于user-mode,则进行GS切换
    SWAPGS                                     ; before: GS -> user; after: GS -> kernel

  Skip_Swap:

    ; Now it's (supposedly) safe to use GS: to access GS-relative kernel
    ; data structures.

    ; 到这里时,操作系统认为GS已经切换到kernel-mode了
     ...                                       ; GS -> kernel

    ; At this point, the ISR switches back to user GS if returning to
    ; user mode; if returning to kernel mode, it leaves kernel GS loaded
    ; and therefore doesn't need to do SWAPGS.

    ; 这里进行测试cpl,如果之前模式为kernel模式,则跳过GS切换
    ; 否则 将GS切换为user-mode
    TEST    [return CS], (1, 2, or 3)          ; GS -> kernel
    JZ      Skip_Swap_Back                     ; GS -> kernel

    SWAPGS                                     ; before: GS -> kernel; after: GS -> user

  Skip_Swap_Back:

    IRETQ                                      ; GS -> user or kernel

由以上代码可知:

如果在异常处理函数的开始处第一个swapgs之前产生一个异常,那么跳入另一个异常处理函数后将不会进行GS切换,因为之前CPU模式为ring 0。如果进入第一个异常处理函数之前的CPU模式为user-mode,则会产生安全漏洞,因为此时CPU处于ring 0, 但gs还没有进行切换,位于user-mode。

同样在异常处理函数结尾,执行完swapgs之后发生异常,也会产生相同漏洞。 如果在用户模式下可以使内核在以上区域发生一个异常,将触发漏洞。 VMware模拟的指令可以在以上区域触发一个异常,可引发漏洞。

Flaw #1(CVE-2008-4279):

x64体系定义规范(Canonical)地址的第48到63位必须是第47位的副本,否则为非规范地址,访问非规范(Non-Canonical)地址会触发#GP异常。

正常情况: jmp [xxx],发生#GP时,TrapFrame的rip为发生异常时指令的地址。 但VMware 模拟的jmp [xxx] 间接跳转指令存在缺陷,发生#GP时,TrapFrame的rip为 jmp 的目标地址(即non-canonical地址)。

x64 Windows下的#GP异常,不会调用 iretq返回到user-mode的non-canonical地址,而是通过KiExceptionDispatch进行异常分发处理,返回到user-mode的ntdll的异常处理函数中。

尽管这样,当重复执行一个 jmp [non-canonical]时,最终将在non-canonical地址处产生一个硬件中断。这样在硬件中断处理函数处理完成执行iretq时,返回的rip为non-canonical地址,触发一个#GP异常, 由于此时已经将GS切换到user-mode,但CPU运行在ring 0,引发漏洞。

注意: 此漏洞不会发生在开启了“禁用加速”的情况下。

参考