漏洞描述
漏洞发生在KiTrap06函数中, 当“BOP”发生时,此函数就会取出CONTEXT结构里保存的环境,返回到NtVdmControl函数,最后回到ntvdm.exe的用户态部分进行处理。但是因为任何进程都可以读写ntvdm.exe进程地址空间中保存的CONTEXT结构中的数据,而KiTrap06并未验证环境结构的有效性,导致可以修改保存在CONTEXT结构里的返回地址和代码段选择子,进而在内核态执行用户态提供的代码来进行本地权限提升。
漏洞分析
NtVdmControl函数的调用参数为VdmStartExecution,也就是0时,就会向V86模式切换。通过调用VdmpStartExecution函数把要执行的V86代码的环境改写内核堆栈中的TrapFrame结构,这样在系统调用KiSystemService返回时并不会返回到NtVdmControl函数里,而是切换进了V86模式。
NTSTATUS
NTAPI
VdmpStartExecution(VOID)
{
....
// 此函数会改写KiFastCallEntry建立的陷阱帧
VdmSwapContext(VdmFrame, &VdmTib->MonitorContext,&VdmContext);
....
return VdmFrame->Eax;
}
而在nt!VdmSwapContexts函数中会修改KiFastCallEntry创建的陷阱帧(trap frame)。 将trap frame修改为我们的VdmTib.VdmContext.
nt!VdmSwapContexts(PKTRAP_FRAME TrapFrame,PCONTEXT MonitorContext, PCONTEXT VdmContext) {
TrapFrame->SegCs = VdmContext->SegCs;
TrapFrame->HardwareSegSs = VdmContext->SegSs;
TrapFrame->Eax = VdmContext->Eax;
TrapFrame->Ebx = VdmContext->Ebx;
TrapFrame->Ecx = VdmContext->Ecx;
TrapFrame->Edx = VdmContext->Edx;
TrapFrame->Esi = VdmContext->Esi;
TrapFrame->Edi = VdmContext->Edi;
TrapFrame->Ebp = VdmContext->Ebp;
TrapFrame->HardwareEsp = VdmContext->Esp;
TrapFrame->Eip = VdmContext->Eip;
TrapFrame->SegCs |= RPL_MASK;
TrapFrame->HardwareSegSs |= RPL_MASK;
/\*Check for bogus CS \*/
if(TrapFrame->SegCs < KGDT_R0_CODE) {
/\* Set user-mode \*/
TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
}
}
DOS环境模拟代码通过一串无效的操作码\xc4\xc4\xXX\xXX,微软称为“BOP”,来传递请求到VDM进程里。传递过程由内核的第6号中断,也就是处理无效操作码的异常处理程序KiTrap06来完成。通过判断引发异常的操作码为“BOP”,内核会分派到虚拟机监控进程ntvdm.exe中。
漏洞发生于异常处理程序KiTrap06分派运行于V86下的代码产生的“BOP”到监控进程ntvdm.exe的过程中。内核处理虚拟机控制的系统调用NtVdmControl的过程中,会把当前系统环境保存在ntvdm.exe的地址空间中保存线程信息的VDM_TIB结构中的CONTEXT结构里。该结构在Windows2000下位于EPROCESS(ntvdm.exe)->VdmObjects->VdmTib->MonitorContext,也就是(((EPROCESS+0x1dc)+0x98)+0xa04),在其它Windows NT系列里位于fs:[0xf18]->MonitorContext,也就是((fs:[0xf18])+0xa04)。
当“BOP”发生后,就会取出CONTEXT结构里保存的环境,返回到NtVdmControl函数,最后回到ntvdm.exe的用户态部分进行处理。但是因为任何进程都可以读写ntvdm.exe进程地址空间中保存的CONTEXT结构中的数据,而KiTrap06并未验证环境结构的有效性,导致可以修改保存在CONTEXT结构里的返回地址和代码段选择子,进而在内核态执行用户态提供的代码来进行本地提升权限。