zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Eclipse配置问题
    什么是SpringMvc
    【转载·】Linux yum 安装 gcc 、gcc-c++
    【转载】linux下安装wget命令(sftp实现法)
    [原创]关于javax.servlet.ServletException: File [/loginController/getVerifCode.jsp] not found异常 解决方案
    【转载】spring boot 链接 虚拟机(Linux) redis
    【转载】spring-boot 项目跳转到JSP页面
    [原创]Linux 下 redis 链接一次
    Linux 下安装 redis 详情
    [转载]CentOS 7虚拟机下设置固定IP详解
  • 原文地址:https://www.cnblogs.com/DreamoneOnly/p/13300318.html
Copyright © 2011-2022 走看看