CVE-2021-33739&CVE-2021-26868 内核漏洞分析

阅读量345401

|评论3

|

发布时间 : 2021-06-28 10:00:04

 

CVE-2021-33739

漏洞简介

释放CInteractionTrackerMarshaler对象时,只是清除了objChannel保存的对象数组指针,但是没有清除CInteractionTrackerBindingManagerMarshaler对象指向CInteractionTrackerMarshaler地址的指针,导致UAF漏洞。
近年来dwm组件相关的漏洞被频繁爆出,但是笔者没发现有一篇较为详细的从poc到利用,较为完整的分析文章,因此决定用最新的一枚dwm模块漏洞来分析,篇幅较长,望耐心看完

漏洞分析

该漏洞为UAF类型的漏洞,顾名思义,User After Free,要想触发漏洞需要重点观察的地方有两点

  • 找到free对象的地方
  • free之后再次访问被free掉对象的地方

正常情况

申请一个CInteractionTrackerBindingManagerMarshaler对象,两个CInteractionTrackerMarshaler对象,并将CInteractionTrackerMarshaler绑定到CInteractionTrackerBindingManagerMarshaler上面

调用SetResourceBufferProperty之前,可以看到CInteractionTrackerBindingManagerMarshaler对象0x38处为空

调用SetResourceBufferProperty之后,发现看到CInteractionTrackerBindingManagerMarshaler对象0x38处存放了一个指针,该指针指向的一个数组,数组保存着两个被绑定的CInteractionTrackerMarshaler对象,且两个CInteractionTrackerMarshaler对象0x190处存放这引用自己的对象,也就是CInteractionTrackerBindingManagerMarshaler对象

绑定之后,我们手动释放两个CInteractionTrackerMarshaler对象,下图为释放之前,可以看到是Allocated状态

下图为释放之后,可以看到是已经是Free状态了,且其父对象(其引用对象CInteractionTrackerBindingManagerMarshaler)的0x38处保存的指针不变,但是指向的内容确已经清空

漏洞场景

漏洞场景较正常情况,只是多了一步,那就是在手动释放之前,将构造szBuffer[2]的值改为0并调用SetResourceBufferProperty

第二次调用SetResourceBufferProperty之前,可以看到情况与正常情况下一致

key

第二次调用SetResourceBufferProperty之后,我们发现两个CInteractionTrackerMarshaler对象的0x190处的父对象被填为空了,也就是这两个对象当前没有被其他对象引用

CInteractionTrackerMarshaler释放之前,可以看到CInteractionTrackerBindingManagerMarshaler对象0x38处的指针,指向的数组如下

且CInteractionTrackerMarshaler释放之后,可以看到CInteractionTrackerBindingManagerMarshaler对象0x38处的指针,指向的数组并没有被清空,这就导致了父对象0x38偏移处保持了两个已经被释放的对象

why

对比正常情况和漏洞场景我们发现,只是简单的调用了一次SetResourceBufferProperty,且设置不了不同的szBuffer,怎么就会不一样了?IDA中观看SetResourceBufferProperty更加直观一点,我们可以看到 v9 = (_DWORD )(szBuff + 8)

后续对v9进行了判断,如果v9为空,那么则会调用RemoveBindingManagerReferenceFromTrackerIfNecessary函数,该函数将会将把CInteractionTrackerMarshaler对象0x190处置空

在之后构造nCmdReleaseResource,进行释放资源时最终会调用到DirectComposition::CApplicationChannel::ReleaseResource函数,最终调用到DirectComposition::CInteractionTrackerMarshaler::ReleaseAllReferences函数,该函数最终会对CInteractionTrackerMarshaler对象0x190处做解析,如果这里为空则不会释放其父对象CInteractionTrackerBindingManagerMarshaler偏移0x38处的指针,因此在我们第二次调用SetResourceBufferProperty后,给我们保留了一个不正常的CInteractionTrackerBindingManagerMarshaler对象(对象0x38偏移处保存了两个已经被释放的对象)

漏洞触发

在漏洞分析部分我们已经找到free对象的地方,现在我们迫切的需要一个能访问到被释放对象的函数

可以看到CInteractionTrackerMarshaler对象0x18处为我们指定的HANDLE标识

我们回头继续看SetBufferProperty函数,该函数第一个参数为objCInteractionTrackerBindingManagerMarshaler,第二个参数为objChannel

经过对objChannel的逆向,其结构体部分内容如下

通过handle为索引通过obChannle偏移0x38处,获取对应的CInteractionTrackerMarshaler对象 ,以下简称为A对象和B对象

我们可以看到v21是从数组里面取出来的第一个CInteractionTrackerMarshaler对象,这里会分别对比A对象的handle和从数组里取出来第一个对象的handle,以及B对象的handle和从数组里取出的第二个对象的handle进行对比,如果一样,则只是设置szBuffer[2]的内容到数组中

那么我们在CInteractionTrackerMarshaler对象被释放后,构造Fake_CInteractionTrackerMarshaler对象,使其handle分别为0x4和0x5,占用原先的内核地址空间

然后在创建handle为4和5的CInteractionTrackerMarshaler对象,最后调用NtDCompositionCommitChannel函数去触发BSOD

BSOD内容,现在我们已经有了free之后再次访问被free掉对象的地方。win32kbase!DirectComposition::CInteractionTrackerBindingManagerMarshaler::EmitBoundTrackerMarshalerUpdateCommands+0x4f

漏洞利用

要想成功利用漏洞则要具备WWW条件,Write What Where

我们要寻找写入点,写什么,写到哪

通过漏洞触发,我们锁定到了UAF中的Use部分,指向数组的指针偏移为0x10处不能为0,否则来不到调用处,因此之前我们最后一次调用SetResourceBufferProperty设置的szBuffer[2]为0xffff。

由下图我们可以看到在取出objCInteractionTrackerMarshaler对象,先取出objCInteractionTrackerMarshaler对象0x0处的内容,改地址为虚表指针,再取出虚表0x50处的函数进行调用

我们可以通过映射0xffffffff位置,构造一个虚表,在虚表0x50处放入我们想要调用的函数

通常我们要用到一些小函数实现任意写,内核中就有这么一个函数SeSetAccessStateGenericMapping,该函数会将rdx放到[rcx+0x48]+0x8处

由此Fake_CInteractionTrackerMarshaler的0x48处为我们的 where,rdx为我们的what,Palette对象(Fake_CInteractionTrackerMarshaler)构造如下,Fake_CInteractionTrackerMarshaler的0x0处为映射的0xffffffff指针,Fake_CInteractionTrackerMarshaler的0x018处为handle,Fake_CInteractionTrackerMarshaler的0x48处为where,objChannel+0xb8处为what,该地址保存着这样一个值为0x000000000000000~0xffff….,但是我们是一个16字节的写,至此我们有了一个任意16字节内存破坏的利用链

where的选取

每个线程都有自己的_KTHREAD,该结构体部分截图如下,我们可以尝试将where定位在PreviousMode附近,该字段表示当前线程的当前模式,1为usermode,0为kernelmode,我们只要将PreviousMode改为0,即可利用kernelmode的权限进行一下API的操作,这些API在kernelmode下有更多的权限,从而导致LPE

EXP完整利用过程

漏洞利用第一步,释放对象

为了利用漏洞我们首先先利用nCmdCreateResource创建3个对象,分别为一个CInteractionTrackerBindingManagerMarshaler和两个CInteractionTrackerMarshaler。并将handle为2 和 3的CInteractionTrackerMarshaler对象绑定到CInteractionTrackerBindingManagerMarshaler上,将handle为4 和5的CInteractionTrackerMarshaler对象留着备用

为了创建上述对象,需要调用内核态函数NtDCompositionProcessChannelBatchBuffer数,该函数内部由DirectComposition::CApplicationChannel::ProcessCommandBufferIterator分发,当为nCmdCreateResource时则会调用DirectComposition::CApplicationChannel::CreateResource函数

DirectComposition::CApplicationChannel::CreateResource函数内部继续调用CreateInternalResource进行不同资源的创建,分别会于win32kbase!DirectComposition::CApplicationChannel::CreateInternalResource+0x9c453以及win32kbase!DirectComposition::CApplicationChannel::CreateInternalResource+0x9c479分别申请 CInteractionTrackerBindingManagerMarshaler对象、CInteractionTrackerMarshaler对象

动态创建对象,用windbg下如下断点,其中伪寄存器$t1的值为当前进程的EPROCESS,可以看到下图所示,我们分别打印出了这些被创建对象的内核地址空间

ba e1 win32kbase!DirectComposition::CApplicationChannel::CreateInternalResource+0x9c453 ".if $proc==$t1 {.printf \"申请的CInteractionTrackerBindingManagerMarshaler对象 地址为=%p\\r\\n\",@rax;gc} .else{.printf \"pass\\r\\n\" ;gc}"
ba e1 win32kbase!DirectComposition::CApplicationChannel::CreateInternalResource+0x9c479 ".if $proc==$t1 {.printf \"申请的CInteractionTrackerMarshaler对象 地址为=%p\\r\\n\",@rax;gc} .else{.printf \"pass\\r\\n\" ;gc}"
ba e1 win32kbase!NtDCompositionProcessChannelBatchBuffer+0x1a1 ".if $proc==$t1 {.printf \"消息分发\\r\\n\"} .else{.printf \"pass\\r\\n\" ;gc}"

创建之后,构造MappedAddress,设置nCmdSetResourceBufferProperty,并调用NtDCompositionProcessChannelBatchBuffer,将hande为2和3的对象绑定到CInteractionTrackerMarshaler对象绑定到CInteractionTrackerBindingManagerMarshaler上,从下图可以看到绑定之前和绑定之后在CInteractionTrackerBindingManagerMarshaler内核地址空间偏移0x38处保存了一个指针,该指针指向一个数组,数组中的值分别为之前被绑定的CInteractionTrackerMarshaler对象

再次构造MappedAddress,设置nCmdSetResourceBufferProperty,并调用NtDCompositionProcessChannelBatchBuffer,这次较上次调用的区别如下红色框

可以看到handle为2和3的CInteractionTrackerMarshaler对象,分别简称为A,B对象。在调用SetBufferProperty函数之前,其父对象(引用该对象的对象)都为绑定的CInteractionTrackerBindingManagerMarshaler对象

通过将szBuff[3]设置为0x00,调用SetBufferProperty函数之后,可以看到其父对象(引用该对象的对象)已经为空

继续构造MappedAddress,设置nCmdReleaseResource,并调用NtDCompositionProcessChannelBatchBuffer,在调用之前可以看到CInteractionTrackerMarshaler对象依旧是Allocted状态

在调用之前可以后CInteractionTrackerMarshaler已经变为了Free状态

释放之后回到应用态是用大量的Palette对象占用该地址空间

可以看到由win32k类型的Palette对象成功占位

可看到我们构造的Fake_CInteractionTrackerMarshaler(Palette)对象,成功占位

继续用用两个handle分别为0x4和0x5的对象,并将szBuff[2]设置为0xffff ,构造MappedAddress

调用SetBufferProperty可以看到原先的CInteractionTrackerBindingManagerMarshaler这个对象偏移0x38处并未被填空,其保存的指针依旧为指向一个数组,内容为原先的handle为两个已经释放掉的CInteractionTrackerMarshaler对象,

在占位到CInteractionTrackerMarshaler对象后,我们紧接着调用NtDCompositionCommitChannel函数,最终调用DirectComposition::CInteractionTrackerBindingManagerMarshaler::EmitBoundTrackerMarshalerUpdateCommands函数

利用构造的虚假虚表,调用SeSetAccessStateGenericMapping函数

SeSetAccessStateGenericMapping会进行十六字节的写操作

替换之前PreviousMode为1

替换后PreviousMode为0

接着就是注入Winlogon进程,成功获取system权限

 

附录

分析中用到的exp地址

https://github.com/mavillon1/CVE-2021-33739-POC

实验环境为

windows 10 1909 x64

原exp公开的时期为2021年4月份,其实包含了两个漏洞,exp作者应该也不知道,微软也不知道,微软只修复了内核中的漏洞,并没有修复应用层的漏洞,应用层漏洞的触发只需要szBuffer[0]和szBuffer[1]中的保存的handle一样即可。

本文由与时尽现。原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/245427

安全KER - 有思想的安全新媒体

分享到:微信
+14赞
收藏
与时尽现。
分享到:微信

发表评论

Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全KER All Rights Reserved 京ICP备08010314号-66