Intel Sysret --- CVE-2012-0217

Posted dreamoneonly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Intel Sysret --- CVE-2012-0217相关的知识,希望对你有一定的参考价值。

CVE-2012-0217

漏洞成因

Intel CPU中的sysret指令在返回的三环地址是不规范地址时会产生#GP异常

canonical addresses:

? 0x0000000000000000-0x00007fffffffffff

? 0xffff800000000000-0xffffffffffffffff

AMD64中只实现了48位有效的虚拟地址,因此处理器规定高16位必须根据第47位的符号位进行符号扩展,这样的地址才被称为canonical addresses

IF (CS.L =? 1 ) or (IA32_EFER.LMA =? 1) or (IA32_EFER.SCE =? 1)
(* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *)
    THEN #UD; FI;
IF (CPL =? 0) THEN #GP(0); FI;
IF (operand size is 64-bit)
    THEN (* Return to 64-Bit Mode *)
        IF (RCX is not canonical) THEN #GP(0);
        RIP ← RCX;
    ELSE (* Return to Compatibility Mode *)
        RIP ← ECX;
.......

但是由于sysret指令并不负责栈切换和GS切换,因此在产生#GP异常时栈已经被切换为用户态的栈并且GS已经指向了teb,由于执行#GP异常处理时仍然在0环,因此存在利用栈数据或teb数据有进行内核代码执行的可能

技术图片

Windows UMS中可以通过修改SchedulerProcuncanonical地址,来导致sysret时产生#GP异常

UMS浅析

  • 跟踪EnterUmsSchedulingMode函数

    EnterUmsSchedulingMode->RtlpAttachThreadToUmsCompletionList中发现一处比较重要的NtSetInformationThread调用

技术图片

技术图片

? 在NtSetInformationThread (class information = 31)->PspAttachThreadToUmsCompletionList->KeInitializeUmsThread中存在初始化_KThread->_UMS_CONTROL_BLOCK相关代码

? 填充_KThread->_UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT,并且_RTL_UMS_CONTEXT为三环地址,因此这里存在从三环修改_RTL_UMS_CONTEXT->_CONTEXT->Rip的可能

__int64 __fastcall KeInitializeUmsThread(char *Kthread, int a2, __int64 a3, __int64 a4, __int64 a5)
{
    ...
    
   _UMS_CONTROL_BLOCK = (char *)ExAllocatePoolWithTag(NonPagedPool, 0x98ui64, ‘smUK‘);
    
    ...
    
   _UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT = a5  //a5从三环NtSetInformationThread传递而来
   
    ...
       
   _KThread->ucb = _UMS_CONTROL_BLOCK;
    
    ...
}

技术图片

? 在EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap中填充了_KThread->_UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT中的Rip Rsp等成员,上面已经提到过_KThread->_UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT这是个三环地址,因此我们可以在这里做一个hook,替换Rip为一个uncanonical address

技术图片

? 通过调试可以看到EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap中的Context_KThread->_UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT是同一个物理页的不同映射,因此设置_RTL_UMS_CONTEXT->_CONTEXT->Rip并不是通过系统调用进入内核设置而是直接在环三直接进行设置

技术图片

  • EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap设置完Rip Rsp Rbp R11后,会通过call r11调用我们设置的UMS scheduler Proc,在我们设置的调度程序中通过调用ExecuteUmsThread执行当前被调度的Ums worker Thread,然后我们在Ums worker Thread中调用UmsThreadYield后会随即进入内核执行ZwUmsThreadYield,进行一系列分发后会通过sysret返回至_KThread->_UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT->_CONTEXT->Rip所指向的位置,即在EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap中设置的Rip(loc_77480493),因此我们可以通过hook这个位置改写RipUncanonical address来产生一个#GP异常
  • 如下MSDN所说:
    • 当一个UMS工作线程调用UmsThreadYield或进入系统调用、缺页中断处理时会进入KiUmsCallEntryKiUmsTrapEntry并最终通过sysret返回至UMS scheduler Proc
    • 当一个非UMS线程通过调用ExecuteUmsThread转换为一个UMS调度线程从而调用UMS scheduler Proc,调用时机在上面已经提到过,是在EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap中进行调用的

https://docs.microsoft.com/en-us/windows/win32/procthread/user-mode-scheduling#ums-best-practices

An application‘s scheduler entry point function is implemented as a UmsSchedulerProc function. The system calls the application‘s scheduler entry point function at the following times:

  • When a non-UMS thread is converted to a UMS scheduler thread by calling EnterUmsSchedulingMode.
  • When a UMS worker thread calls UmsThreadYield.
  • When a UMS worker thread blocks on a system service such as a system call or a page fault.
  • 接下来我们来分析一下当UMS worker Thread调用UmsThreadYield后是如何返回至用户层UMS scheduler Proc的,通过调用UmsThreadYield其实是等价于在系统调用中进入KiUmsCallEntry,因为ZwUmsThreadYield也只是简单的调用了KiServiceInternal
void ZwUmsThreadYield()
{
  unsigned __int64 v0; // rt0

  _disable();
  v0 = __readeflags();
  KiServiceInternal();
}

技术图片

  • KiUmsCallEntryKiUmsTrapEntry基本一致,我们这里以KiUmsCallEntry为例分析,在KiUmsCallEntry->KiSwapToUmsThread->KeBuildPrimaryThreadContext中通过读取之前在EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap设置的KThread->_UMS_CONTROL_BLOCK->_RTL_UMS_CONTEXT->_CONTEXT并将其写入栈上的TrapFrame
__int64 __fastcall KeBuildPrimaryThreadContext(char *a1, __int64 _rbp, __int64 a3, int a4, __int64 a5, __int64 a6)
{
  __int64 v6; // rbx
  __int64 v7; // r9
  _RTL_UMS_CONTEXT *UmsContext; // r11
  unsigned __int64 v9; // rcx
  unsigned __int64 v10; // rcx
  unsigned __int64 v11; // rcx
  unsigned __int64 v12; // rcx
  _KTRAP_FRAME *TrapFrame; // rdx
  _QWORD *v14; // rcx
  unsigned __int64 v15; // rcx
  unsigned __int64 v16; // rcx
  unsigned __int64 v17; // rcx
  unsigned __int64 v18; // rcx

  ...	
    
  UmsContext = (_RTL_UMS_CONTEXT *)**((_QWORD **)a1 + 0x37);   //_KThread->Ucb->UmsContext

  ... 
    
  else
  {
    TrapFrame = *(_KTRAP_FRAME **)(_rbp + 0x50);
    v14 = *(_QWORD **)(v7 + 88);
    TrapFrame->Rip = UmsContext->Context.Rip;
    TrapFrame->Rsp = UmsContext->Context.Rsp;
    TrapFrame->Rbp = UmsContext->Context.Rbp;
    TrapFrame->SegCs = 51;
    TrapFrame->SegSs = 43;
	
    ...
      
      
  return 0i64;
}

然后通过KiUmsCallEntry->KiUmsFastReturnToUser返回

技术图片

技术图片

至此整个UMS的调度流程已经粗略的分析完了,如果我们之前在EnterUmsSchedulingMode->RtlpUmsPrimaryContextWrap改写了Rip,在这里就能成功的通过sysret产生一个#GP异常

利用浅析

我们注意到当sysret产生#GP异常时已经执行了swapgs并且栈也已经切换,gs即指向的是用户态的teb,我们来跟进一下#GP异常处理函数KiGeneralProtectionFault

技术图片

KiGeneralProtectionFault->KiExceptionDispatch->KiDispatchException->KeBugCheckEx->RtlCaptureContext中会取出gs:[0x20]中的值作为一个指针并向其写入内容,由于#GP异常是在内核中产生的,因此在KiGeneralProtectionFault中检查cs是来自内核态的调用后不会调用swapgs,所以现在的gs仍然是指向的teb,而teb[0x20]位置处明显不是一个指针,因此这里会产生一个#PF异常

技术图片

技术图片

在页面错误处理程序KiPageFault我们可以通过申请零地址页面来控制某些执行流程,通过布置零地址偏移0x4C处的数据来绕过KiUmsTrapFrame

技术图片

并最终到达KiCheckForKernelApcDelivery,通过call r11调用内核APC,通过观察可以发现r11就是零地址偏移0x10的位置,因此我们只需要将shellcode的地址填入零地址偏移0x10的位置即可执行内核权限代码

技术图片

技术图片

实验

技术图片

参考

https://www.52pojie.cn/forum.php?mod=viewthread&tid=174982

https://docs.microsoft.com/en-us/windows/win32/procthread/user-mode-scheduling#ums-best-practices

https://xenproject.org/2012/06/13/the-intel-sysret-privilege-escalation/

https://github.com/SecWiki/windows-kernel-exploits/tree/master/MS12-042

以上是关于Intel Sysret --- CVE-2012-0217的主要内容,如果未能解决你的问题,请参考以下文章

CVE-2012-1889漏洞分析报告

cve-2012-5613 mysql本地提权

Mysql身份认证绕过漏洞(CVE-2012-2122)复现

Android内核sys_setresuid() Patch提权(CVE-2012-6422)

android_rooting_tools 项目介绍(CVE-2012-4220)

CVE-2012-0002(MS12-020)3389远程溢出漏洞