win32k.sys缝隙挖掘思路解读

颁布功夫 2020-05-09

一、钻研布景


4月1日,,,,,以色列安全钻研员Gil Dabah在博客上颁布了一篇关于win32k缝隙钻研文章,,,,,描述了若何通过内查对象的Destroy函数和win32k user-mode callback缓解措施的个性来寻找UAF缝隙的新思路。。。。。。。


为此,,,,,8827太阳集团ADLab对win32k有关内核机造进行钻研分析,,,,,并对这类缝隙的挖掘思路进行具体解读分析。。。。。。。


二、win32k缝隙缓解与匹敌


2.1 win32k user-mode callback缝隙


由于设计原因,,,,,win32k驱动必要处置好多用户层的回调,,,,,这些回调给win32k�?? ??? ?榈陌踩戳思却蟮囊迹�,,,,并在从前10年功夫贡献了大量的缝隙。。。。。。。


为了便于缝霞述,,,,,以如下伪代码进行举例分析。。。。。。。


NtUserSysCall()

 {

     PWND p = CreateWindowEx(…);

     somecallback();

     xxxSetWindowStyle(p);

 }


上述代码执行成效如下图所示,,,,,用户层执行的某函数通过syscall传入内核层,,,,,当内核层代码执行到somecallback这一句时,,,,,用户层能够在用户界说的callback函数中获得代码执行的机遇,,,,,若是用户在callback函数挪用了DestroyWindow函数销毁窗口p,,,,,内核层的相应销毁代码将会被执行,,,,,p的相应内存被开释,,,,,回调执行结束,,,,,NtUserSysCall函数持续执行,,,,,当执行到xxxSetWindowStyle(p)一句时,,,,,由于p的内存已经被开释从而导致UAF缝隙的产生。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


2.2 user-mode callback缝隙缓解机造


为了预防上述问题的产生,,,,,微软在对象中引入了一个引用计数(对象+0x8处),,,,,对象分配时引用计数为1,,,,,当执行对象的Destroy函数时引用计数减1,,,,,当引用计数为0时对象会被真正开释。。。。。。。微软通过锁的概想为对象增长和削减引用计数,,,,,在win32k中为对象治理引用计数的锁有两种别离是一时锁(相应函数为ThreadLock/ ThreadUnlock)和永远锁(相应函数为HMAssignmentLock/ HMAssignmentUnlock)。。。。。。。经过加固之后世码阐发为如下大局:


NtUserSysCall()

 {

     PWND p = CreateWindowEx(…);

     ThreadLock(p);

     Somecallback();

     xxxSetWindowStyle(p);

     ThreadUnlock();

 }


通过上述代码,,,,,能够保障即便callback被执行,,,,,p在xxxSetWindowStyle函数执行的时辰也不会被开释。。。。。。。


2.3缓解机造的匹敌技术


上一节提到了对象的引用计数,,,,,若是对象的引用计数为正,,,,,即便执行对象的destroy函数,,,,,对象没有真正被开释,,,,,仍旧存留在内存中,,,,,这种对象被微软开发者称为僵尸(Zombie)对象。。。。。。。一旦僵尸对象的引用计数削减到0它将会隐没,,,,,但是在此之前它仍旧存在内存中,,,,,只是用户层无法接见该对象。。。。。。。


同时为了预防僵尸对象持续存留在内存中,,,,,锁的开释函数(ThreadUnlock/ HMAssignmentUnlock)通常会蕴含对象的开释环节。。。。。。。


对象的Destroy函数还有一个个性就是在开释对象的同时,,,,,Destroy函数也会开释对象的子资源,,,,,其过程能够简要描述如下。。。。。。。


void xxxDestroyWindow(PWND pwnd)

 {

     xxxFW_DestroyAllChildren(); // Destroy child windows, if exist!

     if (NULL != pwnd->spmenu)  // If there’s a menu, remove and destroy it.

     {

         PMENU tmp = pwnd->spmenu;

         if (HMAssignmentUnlock(&pwnd->spmenu)) // If it’s still locked

           {

               DestroyMenu(tmp); // Try destroying it (it can remain a zombie).

           }

      }

     DereferenceClass(pwnd);

     if (HMMarkObjectDestroy(pwnd)) // Check for zero refs!

           HmFreeObject(pwnd); // Only now free the object and handle pair.

 }


DestroyWindow在第一次挪用时开释子资源,,,,,一旦窗口不再被引用,,,,,句柄治理器就会再次齐全销毁它,,,,,通常情况下,,,,,第二次销毁Destroy函数不会在去向理子资源,,,,,由于第一次已经开释了所有的子资源。。。。。。。


但是事件往往不是这么单一,,,,,事实上即便是一个已经挪用过相应Destroy函数开释的僵尸对象,,,,,依然有机遇对其自身进行一些更改(回调之后内核代码仍会对对象进行一些操作),,,,,我们把这种情况叫做Zombie Reload,,,,,当该僵尸对象由于引用计数为0而被真正开释时,,,,,之前的更改操作将会给内核带来一些隐患。。。。。。。


对于如下代码片段:


ThreadLock(pwnd);

xxxSomeCallback(); // Here we can destroy pwnd from user-mode.

InternalSetTimer(pwnd, ...);  // reuse pwnd without check wether it is destroyed

ThreadUnlock();

SomefunctionUseTimer();   //UAF of Timer


我们在用户层回调中对pwnd执行了Destroy函数,,,,,而后通过InternalSetTimer为之设置了一个计时器,,,,,当ThreadUnlock将pwnd真正开释的时辰,,,,,计时器也将被开释,,,,,那么接下来对计时器的操作将会导致UAF缝隙的产生。。。。。。。


三、案例分析


上一节我们会商了对象的引用计数和锁给对象带来的新的安全隐患,,,,,但是真正的挑战在于我们若何确定一段代码中存在缝隙,,,,,关键点是确保在unlock函数中开释的对象在运行到有问题的代码时其引用计数应该为1,,,,,只有这样我们能力在用户层回调挪用其Destroy函数,,,,,并通过unlock函数将这个对象真正开释掉(上锁的时辰会做+1处置),,,,,这也是我们接下来必要会商的。。。。。。。下面我们通过一个案例来分析缝隙挖掘思路。。。。。。。


3.1缝隙成因


下图是xxxMnOpenHierarchy函数的代码片段。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


图中通过xxxCreateWindowEx能够获得一个返回用户层执行callback函数的机遇,,,,,xxxCreateWindowEx创建的窗口将作为父窗口*(struct tagWND **)(**v3 + 8)(上图红框)的子窗口,,,,,若是我们能够通过ThreadUnlock开释父窗口,,,,,那么子窗口v32也会被开释,,,,,所以当后续的safe_cast_fnid_to_PMENUWND函数将v32作为参数执行时就会产生问题,,,,,值妥贴心的是通过回调开释v32是行不通的,,,,,若是这样xxxCreateWindowEx将会返回0,,,,,无法通过if判断。。。。。。。


这里的问题就在于若何保障父窗口在ThreadUnlock函数执行的时辰引用计数为1,,,,,由于要执行xxxMnOpenHierarchy函数必要将父窗口关联到一个menu窗口上,,,,,此时父窗口和menu窗口将会被一个永远锁锁住!!�。。。。�,,,,下面我们介绍若何绕过永远锁。。。。。。。


3.2 缝隙挖掘思路


首先我们创建了g_hMenuOwner和g_hNewOwner两个窗口,,,,,其中g_hMenuOwner的菜单句柄为hMenu,,,,,它也是g_hNewOwner的所有者。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


在上述创建过程中,,,,,内核通过LockPopuMenu函数别离为hMenu和g_hMenuOwner增长了永远锁,,,,,为了达成开释主张,,,,,这个永远锁必要被绕过。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


8827太阳集团(Macau)股份有限公司-Official website


此时锁和所有者的关系是这样的:


8827太阳集团(Macau)股份有限公司-Official website


接下来我们通过SetWindowsHookEx给窗口增长了WH_CBT钩子,,,,,并让窗口进入新闻循环钟祝。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


SendMessage操作为g_hMenuOwner增长一个一时锁,,,,,由于后续的所有攻击都是在message的回调中进行,,,,,所以对于g_hMenuOwner来说这个一时锁是无法开释的,,,,,若是想要机关一个缝隙利用环境首先必要用一些步骤来绕过它。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website

此刻的情况造成了下图所示:


8827太阳集团(Macau)股份有限公司-Official website


当新闻为HCBT_CREATEWND时,,,,,我们第一次达到xxxMNOpenHierarchy函数内部的xxxCreateWindowEx。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


这里能够通过界说关于HCBT_CREATEWND新闻的处置得到执行用户层回调代码的机遇,,,,,这一步的重要主张是为了获取Menu的Wnd。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


当接管到的新闻为WM_ENTERIDLE时,,,,,我们在窗口的新闻回调中通过PostMessage下发新闻。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


发送新闻后,,,,,驱动法式来到了xxxMNKeyDown函数内部挪用xxxSendMessage处。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


通过WM_NEXTMENU新闻的回调函数起头为LPARAM赋值,,,,,赋值操作是为了批改hMenu的Owner,,,,,这样就能够将Owner的一时锁绕过。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


此时内核会接到销毁menu的新闻,,,,,通过用户层的回调函数返回1阻止menu的销毁。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


xxxMNKeyDown函数通过UnlockPopupMenu将g_hMenuOwner身上的永远锁被去掉。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


取而代之的是g_hNewOwner加上了一个锁,,,,,hMenu的Owner也从g_hMenuOwner造成了g_hNewOwner。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


这时,,,,,锁的关系造成了:

   

8827太阳集团(Macau)股份有限公司-Official website


接下来法式第二次进入到xxxMNOpenHierarchy函数并通过xxxSendMessage发送了新闻。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


此时通过设置WM_INITMENUPOPUP回调来获得用户层执行的机遇,,,,,WM_INITMENUPOPUP回调函数通过SetWindowsHookEx函数设置了一个新的hook,,,,,主张是为了在xxxMnOpenHierarchy函数创建子窗口的时辰获得用户层执行权限。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


xxxMnOpenHierarchy函数持续向下执行,,,,,再次来到xxxCreateWindowEx处。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


xxxCreateWindowEx挪用了刚刚设置的回调函数childMenuHookProc。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


在回调函数childMenuHookProc中,,,,,SendMessage发送了WM_NEXTMENU新闻,,,,,通过该界说该新闻的回调函数再次批改参数LPARAM,,,,,这是为了去掉g_hNewOwner身上的永远锁。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


Menu的Owner关系再次被扭转,,,,,xxxMNKeyDown通过函数UnlockPopMenu去掉g_hNewOwner身上的永远锁。。。。。。。并将这个锁沉新加在了g_hMenuOwner上。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website

8827太阳集团(Macau)股份有限公司-Official website


这个时辰,,,,,所有的锁都已经转移到了g_hMenuOwner身上,,,,,而由于WH_CBT钩子已经被移除,,,,,menu将被弃用,,,,,g_hNewOwner将把新创建的窗口link到自己身上。。。。。。。这个时辰情况造成了下面的样子,,,,,g_hNewOwner身上已经没有必要绕过的锁了。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


接着childMenuHookProc通过SetWindowsHookEx函数又一次设置了回调函数并通过SetWindowLongPtr函数来挪用它,,,,,回调函数销毁了g_hNewOwner和xxxCreateWindowEx天生的新窗口。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


xxxCreateWindowEx返回的值为ffff871b80239130,,,,,这就是xxxCreateWindowEx创建的子窗口。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


接下来就能够通过ThreadUnlock来销毁g_hNewOwner和其新创建的子窗口来得到一个UAF缝隙。。。。。。。


8827太阳集团(Macau)股份有限公司-Official website


四、总 结



本文对win32k缝隙挖掘新思路进行了具体解读,,,,,其中蕴含将unlock函数和对象的Destroy函数的个性关联在一路,,,,,并把对象的子资源作为攻击指标寻找新的攻击面的缝隙挖掘思路。。。。。。。另表,,,,,若何通过对象内部的个性去绕过锁对对象的锁定的思路和技巧,,,,,也极度拥有借鉴意思。。。。。。。