Intel sysret 在64位windows下引发的漏洞
漏洞描述
根据Intel手册描述,其64位 CPU 下的sysret指令不主动进行栈切换,需要由开发人员自己显式切换GS、RBP和 RSP,当以上受影响操作系统由kernel-mode返回user-mode时,首先将GS,RBP和RSP恢复为user-mode的值,然后调用sysret。但是当返回地址为无效地址(有效地址的48-63位必须和第47位一致)时,sysret将会触发#GP异常,此时会跳到KiGeneralProtectionFault执行,由于 此时CPU运行在ring0,但RSP、RBP、GS已经切到user-mode地址空间,若精心构造gs所指向的值,可导致ring0下执行任意代码。
漏洞分析
查看 wrk1.2, base\ntos\ke\amd64\trap.asm 如下代码:
TRAP_ENTRY KiSystemCall64, KiSystemServiceHandler
swapgs ; swap GS base to kernel PCR
mov gs:[PcUserRsp], rsp ; save user stack pointer
mov rsp, gs:[PcRspBase] ; set kernel stack pointer
push KGDT64_R3_DATA or RPL_MASK ; push dummy SS selector
push gs:[PcUserRsp] ; push user stack pointer
push r11 ; push previous EFLAGS
push KGDT64_R3_CODE or RPL_MASK ; push dummy 64-bit CS selector
push rcx ; push return address
mov rcx, r10 ; set first argument value
以上代码为user-mode进入kernel-mode时的栈切换情况。
查看 RESTORE_TRAP_STATE 宏, 返回 ring3时的代码如下:
mov r8, TrRsp[rbp] ; get previous RSP value , 获取用户模式rsp
mov r9, TrRbp[rbp] ; get previous RBP value
xor edx, edx ; scrub volatile integer registers
xor r10, r10 ;
pxor xmm0, xmm0 ; scrub volatile floating registers
pxor xmm1, xmm1 ;
pxor xmm2, xmm2 ;
pxor xmm3, xmm3 ;
pxor xmm4, xmm4 ;
pxor xmm5, xmm5 ;
mov rcx, TrRip[rbp] ; get return address
mov r11, TrEFlags[rbp] ; get previous EFLAGS
mov rbp, r9 ; restore RBP
mov rsp, r8 ; restore RSP , 恢复用户模式rsp
swapgs ; swap GS base to user mode TEB
sysretq ; return from system call to user mode ,如果此时发生异常, 堆栈已切到用户模式空间,但CPU运行在ring0.
漏洞利用
利用User-Mode Scheduling (UMS)修改线程上下文环境。首先CreateUmsCompletionList()创建ums完成列表,然后调用EnterUmsSchedulingMode() (在此函数中会调用RtlpUmsPrimaryContextWrap ,其会将ring3的返回地址保存在 GS:[0x14a0]+0x10+0xF8 中)将自身线程由普通线程转换成UMS线程,我们可以在此时将GS:[0x14a0]+0x10+0xF8 中的值修改为一个无效地址,然后调用 ExecuteUmsThread来执行ums线程,最后,内核在完成一些操作后 会调用 KiUmsFastReturnToUser返回用户层,此函数中将rcx、rbp、rsp恢复为use-mode的值然后调用 sysret。由于sysret的返回地址无效,导致#GP异常, 执行流程:–>KiGeneralProtectionFault –> KiBugCheckDispatch –> KeBugCheckEx –> KiSaveProcessorControlState 在其调用过程中没有发现可利用的 代码执行指令,通过分析,发现可以通过构造 GS:0x20为一个非法值,可以在KiSaveProcessorControlState 函数中触发一个page fault异常,执行流程:KiPageFault –> KiCheckForKernelApcDelivery –> KiDeliverApc (在此函数中发现可利用的代码执行指令call r11)。