CVE-2017-8543微软SEARCH RCE Vulnerability 利用分析

作者: 360漏洞研究院 石磊 分类: Windows安全,漏洞分析,漏洞复现 发布时间: 2022-09-29 07:33

0x00

当 Windows Search 处理内存中的对象时,存在远程执行代码漏洞。成功利用此漏洞的攻击者可能会控制受影响的系统。攻击者可随后安装程序;查看、更改或删除数据;或者创建拥有完全用户权限的新帐户。

为了利用此漏洞,攻击者可能会向 Windows Search 服务发送经特殊设计的消息。有权访问目标计算机的攻击者可能会利用此漏洞提升特权并控制目标计算机。此外,在企业情形中,未经过身份验证的远程攻击者可能会通过 SMB 连接远程触发此漏洞,然后控制目标计算机。

此安全更新程序通过更正 Windows Search 处理内存中对象的方式来修复此漏洞。

0x01

本利用能够成功实现基于2个漏洞点:

<1>. 响应 CPMCiStateInOut(0x000000D9) 请求时存在信息泄露。

从代码中可以看出,在派遣函数中会由对应的实例返回应答的字节长度(nNumberOfBytesToWrite)。而在CPMCiStateInOut请求的响应函数(CRequestServer::DoCiState)中,未对Client传来的值做严格的检测(v13 = *(_DWORD *)(v3 + 208); )。那么Client就可以指定令server返回任意长度响应。

<2>. 若干处构造函数中变量未初始化及后续的不当处理导致任意地址释放漏洞。 例如CPropertyRestriction::CPropertyRestriction:

若CFullPropSpec::CFullPropSpec尚未对 v3 + 12 中的结构赋值就触发异常退出。那么在析构时会调用释放COM对象的函数。

CPropertyRestriction::CPropertyRestriction

         CFullPropSpec::CFullPropSpec

                   UnMarshallWideString

                            CNLBaseException::CNLBaseException

CPropertyRestriction::~CPropertyRestriction

         CFullPropSpec::~CFullPropSpec

CoTaskMemFree

这种情形至少可以造成一个UAF利用。

再如CInternalPropertyRestriction::CInternalPropertyRestriction:

若v4 + 68 未被赋值便触发异常退出,那么在析构时会释放掉对应的类对象。

同样,至少可以作为UAF来利用。

这样的问题还有好几处。

巧妙利用上述两点即可实现任意代码执行。另外还有几处能造成拒绝服务的漏洞点。

0x02

基于上述2个漏洞,利用生成方法有很多种,理论上使用三个线程即可实现利用:

==exp=============

Thread1: CPMConnectIn, CPMCreateQueryIn, wait…

Thread2: CPMConnectIn, CPMCiStateInOut, read Thread1…, wait…

Thread3: CPMConnectIn, pad Thread1’s address for any-free, wait…

Thread2: CPMCreateQueryIn, free Thread1’s xxx, out

Thread3: out

Thread3: CPMConnectIn, pad for ROP, wait…

Thread1: CPMSetBindingsIn, RCE, out

Thread3: out

==================

从功能上说,3个功能线程就够了。但在实践中,是需要启动多次线程(Thread1和Thread2)来获取偏移地址的。

0x03

这个漏洞分析和利用的难度其实多半是在协议数据的构造上,WSP(Windows Search Protocol)实在是个很复杂的协议。仅凭微软官方文档,恐怕很难玩的转它。

在实践中,封装了一个CSearchManager 类来管理连接,创建3个该类的实例代替3个线程(Thread1->pSer1,Thread2->pSer2,Thread3->pSer3)。

Search服务启动时初始化了若干0x13C0大小的服务对象信息块,使用多个连接请求,利用漏洞<1>,即可获取其中一个连接完整的0x13C0信息块结构。其中前0xC0字节为内部结构,后0x1300为请求数据空间。我们可以用偏移0x00处的字段(CRequestServer类的虚表)以及偏移0x08和0x0C处的固定标记来识别此结构,顺便过掉ASLR。在不同线程的0x1300请求数据空间中可以随意加入任何自己的标记数据,来识别对应的线程。另可用偏移0x28处的字段(减掉0x18即可)来定位此信息块在内存中的地址。

在CPMCreateQueryIn 请求后,0xC0+0x5C处会保存一个CVIQuery对象的地址,大小为 0x100。

可以用漏洞<2>释放掉这块内存,并填充上能造成ROP的数据。那么之后可以在CPMSetBindingsIn请求中触发。

0x04

步骤一:

实际代码中,使用pSer1 和 pSer2 循环100次,可以秒得基址,同时得到0xC0+0x5C处CVIQuery对象的地址值。

步骤二:

使用pSer3的CPMConnectIn请求去占位一块0x50大小内存,只要把CPMConnectIn请求偏移0x74处的值设为步骤一中取到的CVIQuery对象的地址值即可,其它位置可以任意赋值。

pSer2发送 CPMCreateQueryIn 请求,触发漏洞<2>,释放掉上述填入的内存地址。

步骤三:

使用pSer3的CPMConnectIn请求填入ROP内容,覆盖步骤二中释放掉的内存。

使用pSer1的CPMSetBindingsIn请求触发ROP。

0x05

复现: