23

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

 3 years ago
source link: https://www.anquanke.com/post/id/245427
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
robots

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一样即可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK