起因:
Google Project Zero團(tuán)隊(duì)的新晉成員James Forshaw在9月30日向微軟提交了名為“Windows: Elevation of Privilege inahcache.sys/NtApphelpCacheControl”的安全問題,并且在Google的漏洞公開期限(90天)后,也就是2014年12月29日(北京時間的12月30日)公開了此問題的細(xì)節(jié)。
針對這個漏洞的爭論很多,很多觀眾在爭論微軟和Google對待安全漏洞的做法,90天公開策略是否合情合理等等,也有很多技術(shù)人員在爭論這個安全問題是否是嚴(yán)格意義上的權(quán)限提升漏洞。后者的一個主要原因是James Forshaw在演示這個漏洞時,是通過此漏洞來劫持UAC默認(rèn)級別下自動提權(quán)的ComputerDefaults程序,從而實(shí)現(xiàn)在默認(rèn)UAC設(shè)置下靜默從中完整性級別啟動高完整性級別的程序。
通常來說,微軟安全響應(yīng)中心(MSRC)不認(rèn)為UAC默認(rèn)級別下中完整性級別到高完整性級別的提示繞過屬于安全漏洞的。
但是,最近MSRC也將一些可以穿透InternetExplorer保護(hù)模式(PM)或增強(qiáng)保護(hù)模式(EPM)沙箱的安全問題(實(shí)際上是從低完整性級別穿透至中完整性級別的問題)作為安全漏洞來修補(bǔ)(例如CVE-2014-6349)。所以我們先擱置是否安全漏洞的這個爭議,深入分析下這個漏洞涉及的原理和問題。
漏洞分析、成因與利用
這個漏洞的基本原因,Google的這篇簡單說明里已經(jīng)講得比較準(zhǔn)確了,簡單來說,就是NtApphelpCacheControl這個系統(tǒng)調(diào)用,在調(diào)用者模擬System權(quán)限時,沒有正確識別調(diào)用者的Token,導(dǎo)致其本來僅提供給管理員和系統(tǒng)程序調(diào)用的接口,可以被低權(quán)限程序誤用,并借助該調(diào)用的相關(guān)機(jī)制,劫持高權(quán)限程序,實(shí)現(xiàn)權(quán)限提升。
看完這個原因后,你可能會有疑問,這個系統(tǒng)調(diào)用是做什么的?什么是Apphelp?Token的判斷具體哪里有問題?Apphelp的哪項(xiàng)機(jī)制如何能被利用、以及是如何劫持高權(quán)限進(jìn)程的?接下來筆者就將深入地介紹這些內(nèi)容,詳細(xì)地解答這些問題。
1. Apphelp與NtApphelpCacheControl
在Windows 8.1 系統(tǒng)上,NtApphelpCacheControl這個系統(tǒng)調(diào)用,顧名思義,可以控制系統(tǒng)中Apphelp規(guī)則的快速緩存數(shù)據(jù)。
Apphelp是微軟從WindowsXP操作系統(tǒng)開始引入的一項(xiàng)兼容性解決方案,官方的名字是”Application Compatibility Database”(應(yīng)用程序兼容性數(shù)據(jù)庫)。這套解決方案的目的是減少操作系統(tǒng)的向下兼容成本,不是在操作系統(tǒng)層面,而是通過這套兼容數(shù)據(jù)庫引擎提供包括Shim(Hook)在內(nèi)的大量控制功能,來做到實(shí)時地識別和修改特定存在兼容性問題的流行軟件程序,使其能夠兼容新的操作系統(tǒng)。
關(guān)于這套機(jī)制,微軟官方從Windows7開始, 有一些功能性的介紹文檔,也可以通過官方的”Microsoft Application Compatibility Toolkit”(ACT),在Windows7以上的操作系統(tǒng)上方便里查看、修改和添加本地的兼容性數(shù)據(jù)庫。在此之前,Alex ionescu和一些其他的國內(nèi)外研究者也深入地研究過兼容性數(shù)據(jù)庫和Shim機(jī)制。
微軟操作系統(tǒng)的各個版本積累了針對應(yīng)用程序的大量兼容性修改的方法和基礎(chǔ)數(shù)據(jù),使這些應(yīng)用程序可以流暢地運(yùn)行在新的操作系統(tǒng)中,這為微軟的操作系統(tǒng)在世界范圍內(nèi)的復(fù)雜環(huán)境中廣泛運(yùn)行和推廣起到了重要的作用。在Windows Vista發(fā)布后,它帶來的海量革新的同時,也導(dǎo)致大量應(yīng)用程序出現(xiàn)兼容性問題,成為該系統(tǒng)被人詬病的原因之一。為此,在其后的Windows7操作系統(tǒng)開發(fā)過程中,微軟加大了對應(yīng)用程序兼容性修復(fù)方面的重視和投入,不僅更廣泛地收集和編制應(yīng)用程序兼容性修復(fù)規(guī)則,也對這套兼容性機(jī)制做了升級換代。
本次出現(xiàn)問題的NtApphelpCacheControl也是支持這套機(jī)制的一個新的接口。這已經(jīng)不是該函數(shù)第一次出現(xiàn)安全漏洞, 在j00ru Syscan2013上的關(guān)于Bochspwn的議題上,就公開了一個涉及NtApphelpCacheControl處理緩存代碼中存在的競爭條件漏洞(CVE-2013-1278)。閱讀下面的內(nèi)容的同時,建議讀者能看一看這個slides中NtApphelpCacheControl相關(guān)的內(nèi)容(75~94頁。
為什么Apphelp需要這個接口呢?這還需要從XP系統(tǒng)說起,應(yīng)用程序兼容性的主數(shù)據(jù)庫實(shí)際存儲是在Windows安裝目錄下的AppPatchsysmain.sdb文件中的,但是每次進(jìn)程啟動、模塊加載時都去檢查這個數(shù)據(jù)文件顯然開銷太大。為了能快速處理針對這些可執(zhí)行程序的兼容性規(guī)則,就需要在內(nèi)存中緩存這個數(shù)據(jù)庫的機(jī)制。在Windows XP時代, Apphelp使用一塊名為ShimSharedMemory的全局共享Section來在不同進(jìn)程之間共享識別、修改可執(zhí)行程序的兼容性數(shù)據(jù)內(nèi)存。
從Windows 2003開始,NtApphelpCacheControl函數(shù)被引入系統(tǒng)調(diào)用中,作為apphelp的內(nèi)核入口,在內(nèi)核中提供了整套新的應(yīng)用程序兼容性數(shù)據(jù)庫檢索、緩存的功能,使得應(yīng)用程序可以跨Session、跨隔離,并且快速查詢、應(yīng)用兼容性數(shù)據(jù)庫中的規(guī)則和處理方案,其緩存功能也實(shí)現(xiàn)了在盡量減少性能消耗的同時,快速地針對系統(tǒng)內(nèi)已知的存在兼容問題的程序進(jìn)行更快速地修復(fù)。
在Windows2003,Windows Vista,Windows7和Windows8操作系統(tǒng)上,NtApphelpCacheControl是實(shí)現(xiàn)在系統(tǒng)內(nèi)核ntoskrnl內(nèi)部的,從Windows8.1操作系統(tǒng)開始,為了能夠減少升級成本,內(nèi)核將該系統(tǒng)調(diào)用的絕大部分最終實(shí)現(xiàn)在了一個新的內(nèi)核模式驅(qū)動程序ahcache.sys內(nèi)部,系統(tǒng)內(nèi)核通過發(fā)送設(shè)備控制命令給該驅(qū)動來實(shí)現(xiàn)該接口的絕大部分功能,在Windows10技術(shù)預(yù)覽版上,我們看到這個驅(qū)動又進(jìn)行了一次比較大的更新。
NtApphelpCacheControl的原型為:
NTSTATUS NtApphelpCacheControl(APPHELPCOMMAND Command , PVOID CommandData);
這個Command提供了針對應(yīng)用程序兼容性數(shù)據(jù)庫緩存的查看、添加、刪除等等一系列功能。其中Command的枚舉功能和CommandData的數(shù)據(jù)結(jié)構(gòu),J00ru的議題和James的源碼中都提供了一些,但都不全面也有不少錯誤,通過IDA查看實(shí)現(xiàn)代碼很容易就可以看清楚,這里我將這些功能的Command id、對應(yīng)的控制碼(適用于 Windows8.1)和對應(yīng)實(shí)現(xiàn)的功能整理匯總后列出:
enum APPHELPCOMMAND
{
AppHelpCahceLookup, // IoControlCode: 0x220003*
AppHelpCahceRemove, // IoControlCode: 0x220007*
AppHelpCahceUpdate, // IoControlCode: 0x22000B*
AppHelpCacheFlush // IoControlCode: 0x22000F*
AppHelpCacheDump, // IoControlCode: 0x220013
AppHelpCacheNotifyStart , // IoControlCode: 0x220017
AppHelpCacheNotifyStop, // IoControlCode: 0x22001B
AppHelpCahceForward, // IoControlCode: 0x22001F
AppHelpCacheQuery, // IoControlCode: 0x220023
AppHelpQueryModule, // IoControlCode: 0x220027
AppHelpRefresh, // IoControlCode: 0x22002B
AppHelpCheckForChange, // IoControlCode: 0x22002F
AppHelpQueryHwId,
};
James在源碼中定義了相關(guān)結(jié)構(gòu),不過看上去對這個結(jié)構(gòu)的了解也不是很深入。*:James的POC源碼中,對這四個控制碼少了一個0
AppHelpCacheLookup:在Apphelp cache中尋找匹配的、需要處理的記錄。
AppHelpCacheRemove: 刪除匹配的Apphelp Cache記錄
AppHelpCahceUpdate:插入記錄到Apphelp Cache
AppHelpCacheFlush:Flush AppHelp cache,將AppHelpCache的緩存數(shù)據(jù)清空(根據(jù)Flush的標(biāo)志不同,不一定真的刪除內(nèi)存中的數(shù)據(jù),只是去掉某些標(biāo)記),并刷新到磁盤(注冊表)上。
James源碼中認(rèn)為這個命令是沒用處的AppHelpEnum。這是錯誤的 ,apphelp!ShimFlushAppcompatCache->kernelbase!BaseFlushAppcompatCache->kernel32!BaseFlushAppcompatCacheWorker 還在使用這個來清空shim的兼容數(shù)據(jù)數(shù)據(jù)。他應(yīng)該是將AppHelCacheFlush看漏了,源碼里對于NotifyStartEnum等后面的幾個都差了一個數(shù)。
AppHelpCacheDump:這才是一個“沒用”的功能, AppHelp會枚舉緩存中的數(shù)據(jù),針對枚舉的項(xiàng)目并不做操作。筆者猜測這個命令之所以叫“Dump”,可能是一個調(diào)試功能。于是筆者從WDK中找了個checked build的內(nèi)核來看了下。果然在checked build內(nèi)核中,會枚舉緩存中的元素,并逐個打印出記錄要去匹配的文件名記錄。在Free build中,對應(yīng)的代碼被去掉了,所以成了一個看似無用的命令,實(shí)際在checked build,微軟的開發(fā)人員可能是有對應(yīng)的工具去通過這個接口方便地觀察緩存中都有那些記錄。
AppHelpCacheNotifyStart/AppHelpCacheNotifyStop:這兩個命令主要用于同AppHelp的服務(wù)通訊,AppHelp內(nèi)核會通過AhcPort這個ALPC Port同Application Experience服務(wù)通訊。這兩個命令用于重連/停止 同服務(wù)的LPC通訊。
AppHelpCacheForward: 用于將緩存可執(zhí)行程序的信息排隊(duì),然后轉(zhuǎn)發(fā)給Application Experience服務(wù)處理
AppHelpCacheQuery: 用于獲取Shim Cache中的數(shù)據(jù)內(nèi)容、ShimCache的相關(guān)統(tǒng)計(jì)和隊(duì)列。
AppHelpQueryModule/AppHelpCacheRefresh/AppHelpCheckForChange/AppHelpQueryHwId:這些是Windows8.1新增的命令,和這里就不詳細(xì)介紹了。
下面介紹這個調(diào)用中重要的數(shù)據(jù)CommandBuffer的結(jié)構(gòu),這個結(jié)構(gòu)的數(shù)據(jù)驅(qū)動了Lookup/Remove/Update/Forward等大部分基本命令。
這個CommandBuffer的結(jié)構(gòu)(我這里稱為APPHELP_COMMANDDATA),是由三個結(jié)構(gòu)體組成的:
其中第一個結(jié)構(gòu)體用于存儲整個shim緩存和統(tǒng)計(jì)內(nèi)容,被AppHelpCacheQuery命令使用;
第二個結(jié)構(gòu)體用于在AppHelpCacheLookup/AppHelpCacheRemove/AppHelpCacheUpdate這三個命令時來告訴接口檢索、刪除和添加的項(xiàng)目內(nèi)容;
第三部分則用于AppHelpCacheForward命令,提供forward,用于提供轉(zhuǎn)發(fā)給服務(wù)的相關(guān)數(shù)據(jù)。
無論是哪個命令,CommandBuffer都同時包含著三個結(jié)構(gòu)體,這也是為什么J00ru的議題和James的源碼里都提到提交的緩存結(jié)構(gòu)前面有大量無用的0數(shù)據(jù)的原因。
下面給出這個結(jié)構(gòu)的定義:
typedef struct APPHELP_COMMANDDATA{
APPHELP_CACHE_QUERY QueryData; //Query full data
APPHELP_CACHE_ENTRY EntryData ; //Lookup/Remove/Update entry
APPHELP_CACHE_FORWARD ForwardData; //Data for forward
} APPHELP_COMMANDDATA, *PAPPHELP_COMMANDDATA;
其中APPHELP_CACHE_QUERY/APPHELP_CACHE_FORWARD的結(jié)構(gòu)和本次漏洞無關(guān),就留給感興趣的讀者去研究了。
需要注意的是,QueryData的數(shù)據(jù)長度會影響后面的EntryData(這個長度在Win7Win8上不相同),Win7上是0×90,Win8.1上James已經(jīng)提供了是0×98(這也是為什么Win7上這個POC無法工作的原因之一)。這點(diǎn)看一看ApphelpCacheControlValidateParameters或AhcValidateAndGetParameters就可以了解了。
這里提供一下APPHELP_CACHE_ENTRY的數(shù)據(jù)結(jié)構(gòu):
typedef struct APPHELP_CACHE_ENTRY{
DWORD Flags ;
ULONG CacheReturnFlags ;
HANDLE FileHandle;
HANDLE ProcessHandle ;
//Only in Win8/Win8.1UNICODE_STRING FileName;
UNICODE_STRING PackageFullName;
//only in Win8/Win8.1DWORD DataBufferSize;
PVOID DataBuffer;
} APPHELP_CACHE_ENTRY, *PAPPHELP_CACHE_ENTRY;
FileHandle/FileName是對應(yīng)要進(jìn)行處理的可執(zhí)行程序(包括模塊)的對應(yīng)句柄和文件名,之所以要提供文件句柄,是因?yàn)閮?nèi)核使用一個AVL Table來存儲Entry數(shù)據(jù),使用文件句柄的FileTime來做其中平衡樹的索引。其中 Flags/CacheReturnFlags分別指明了這條Cache的功能和作用,尤其是對于DataBufferSize/DataBuffer是否有效的控制,是否在關(guān)機(jī)時保存到注冊表中等。
DataBufferSize和DataBuffer描述了Entry相關(guān)的規(guī)則數(shù)據(jù)的內(nèi)容。
ProcessHandle和PackageFullName僅在Windows8及以后的操作系統(tǒng)上使用。
DataBuffer可以保存由RING3存儲的數(shù)據(jù)結(jié)構(gòu),一般來說由APPHELP_CACHEUPDATE更新到內(nèi)核緩存中,APPHELP_CACHELOOKUP獲取后使用,Shim引擎使用的數(shù)據(jù)結(jié)構(gòu),我稱為APPHELP_LOOKUP_RESULT(即James源碼中的APPHELP_QUERY結(jié)構(gòu)),如下:
#define MAX_EXE_TAGS 16
#define MAX_LAYER_TAGS 8
typedef struct APPHELP_LOOKUP_RESULT
{
DWORD ExeTags[MAX_EXE_TAGS];
DWORD ExeFlags[MAX_EXE_TAGS];
DWORD LayerTags[MAX_LAYER_TAGS];
DWORD LayerFlags;
DWORD AppHelpTag;
DWORD ExeTagsCount ;
DWORD LayerTagsCount;
GUID ExeGuid;
DWORD Flags2;
DWORD Unknown;
DWORD Unknown2;
GUID Guid2[16];
};
如James源碼中所使用的, 其中ExeTags指明了在sdb數(shù)據(jù)中的修復(fù)方案的tag id,ExeTagsCount是tag的數(shù)量,最多16個, LayerTags是Layer規(guī)則的數(shù)據(jù)。
通過這些命令和數(shù)據(jù)結(jié)構(gòu),我們大概了解了NtAppHelpCacheControl提供的能力,那么RING3是如何使用它的呢?
簡單地說,Ring3的Module Loader等模塊在模塊加載、進(jìn)程啟動等事件觸發(fā)時,會調(diào)用apphelp.dll中的相關(guān)接口, apphelp.dll再調(diào)用kernel32/kernelbase內(nèi)BasepShim*相關(guān)的API,通過NtAppHelpCacheControl中的CacheLookup功能查詢模塊的可執(zhí)行文件是否在緩存的數(shù)據(jù)中,如果存在就按照規(guī)則數(shù)據(jù)庫中對應(yīng)的規(guī)則處理,相關(guān)的數(shù)據(jù)通過CacheForward發(fā)送到服務(wù)進(jìn)程后,會調(diào)用CacheUpdate功能緩存被使用的數(shù)據(jù),加快二次查詢的速度。
Token相關(guān)的問題
了解了AppHelpCache的接口和基本工作原理后,我們很容易可以明白,如果可以低權(quán)限的程序可以調(diào)用AppHelpCacheUpdate命令添加緩存,那么可以劫持任意高權(quán)限程序,利用shim規(guī)則對其進(jìn)行修改,實(shí)現(xiàn)權(quán)限提升。
那么我們回來看看這個漏洞的成因,如James在說明里提到的,這個漏洞的原因是因?yàn)橛糜隍?yàn)證AppHelpCacheUpdate是否運(yùn)行被調(diào)用的AhcVerifyAdminContext函數(shù),驗(yàn)證Token存在問題,這個函數(shù)很簡單,我們使用Hex-rays decomplier得到偽代碼:
NTSTATUS AhcVerifyAdminContext()
{
retstatus = STATUS_ACCESS_DENIED;
CurrentThread = KeGetCurrentThread();
CurrentProcess = PsGetCurrentProcess();
TokenType = 0;
TokenObj = PsReferenceImpersonationToken(CurrentThread, &CopyOnOpen, &EffectiveOnly, &ImpersonationLevel);
if ( TokenObj || (TokenObj = PsReferencePrimaryToken(CurrentProcess), TokenType = 1, TokenObj) )
{
if ( SeQueryInformationToken(TokenObj, 1, &TokenUserInformation) >= 0 )
{
if ( RtlEqualSid(_SeExports->SeLocalSystemSid, TokenUserInformation->User.Sid) || SeTokenIsAdmin(TokenObj) )
{
retstatus = STATUS_SUCCESS;
}
ExFreePoolWithTag(TokenUserInformation, 0);
}
else
{
AhcTracePrintf(0, "AhcVerifyAdminContext", 937, "Failed to query token information.
", v5);
}
if ( TokenObj )
{
if ( TokenType == 1 )
{
PsDereferencePrimaryToken(TokenObj);
}
else
{
PsDereferenceImpersonationToken(TokenObj);
}
}
}
else
{
AhcTracePrintf(0, "AhcVerifyAdminContext", 929, "Failed to get effective token", v5);
}
return retstatus;
}
函數(shù)的功能很簡單,首先試圖獲取線程模擬的token,如果線程的模擬token不存在,就獲取當(dāng)前進(jìn)程的主token對象,獲取token對象后, 通過SeQueryInformationToken(TokenUser)獲得Token的用戶信息。
接著,對比如果下面token對象符合下面兩種情況的任一種,就返回STATUS_SUCCESS允許AppHelpCacheUpdate操作,否則就返回STATUS_ACCESS_DEIND拒絕操作:
1.Token的用戶SID匹配LocalSystem的SID??2.Token通過SeTokenIsAdmin的驗(yàn)證,也就是token用戶組內(nèi)有啟用的Adminsitrator組。
那么這個對比就是James所說的驗(yàn)證Token有問題的部分,有問題的原因就如James所說, 是因?yàn)闆]有去判斷ImpersonationLevel。
這是因?yàn)楸荒M的Token的SID并不能決定Token就真的擁有對應(yīng)SID的權(quán)利,可以參考微軟關(guān)于SECURITY_IMPERSONATION_LEVEL的MSDN解釋。
低于SecurityImpersonation的模擬token其實(shí)并不是以被模擬的Client的安全上下文運(yùn)行的,所以僅僅通過SID來判斷調(diào)用者是否具備LocalSystem的權(quán)限是不夠,類似的問題其實(shí)是Windows的內(nèi)核、內(nèi)核模式驅(qū)動程序中還有不少地方存在,感興趣的讀者可以再進(jìn)行一些挖掘。
以James源碼的方法為例,通過Bits服務(wù)的BackgroundCopyManager接口創(chuàng)建一個下載任務(wù)后,代碼將自己的Notify對象設(shè)置為任務(wù)的通知接口。
此時服務(wù)在下載通知時會調(diào)用對應(yīng)的對象,接著combase通過LPC回調(diào)試圖調(diào)用對象的接口函數(shù),而在調(diào)用前,就會使用RpcImpersonateClient->NtAlpcImpersonateClientOfPort,由AlpcpImpersonateMessage來為執(zhí)行回調(diào)接口準(zhǔn)備Alpc port指定的安全上下文。
由于Bits的OLE Port指定的允許的SecurityQos->ImpersonationLevel級別的模擬,模擬完成后調(diào)用到James的處理代碼,他的代碼打開并保存了線程的token句柄,由此獲得了一個SecurityIdentification級別的Token句柄 ,Token是來自Bits服務(wù)的安全上下文,因此Token的SID自然也就是NT AUTHORITYSYSTEM(LocalSystem)
這里Bits服務(wù)本身是沒有安全問題的,因?yàn)锳LPC獲得和模擬的token只是SecurityIdentification級別,即使模擬這個token(就如James的代碼后面所做的),也無法以System權(quán)限上下文工作,在訪問對象ACL時,會被拒絕訪問(可以參考SeAccessCheck中的實(shí)現(xiàn)和判斷),也不會被識別為admin/system(可以參考SeTokenIsAdmin在Windows7以上操作系統(tǒng)的實(shí)現(xiàn)),但是NtAppHelpCacheControl這里只判斷Token SID的方式,就導(dǎo)致了安全檢查被繞過,也是這個漏洞的根本原因。
漏洞的利用
James提供的POC針對這個漏洞利用的方法是劫持一個UAC默認(rèn)級別下會不區(qū)分命令行,無提示自動提權(quán)的程序ComputerDefaults.exe,找到regsv32修復(fù)的的sdb tag(通過替換模塊鏡像為regsvr32來進(jìn)行兼容修復(fù)),并將其設(shè)置到apphelp緩存中,這樣,在啟動ComputerDefaults.exe的時候,實(shí)際啟動的可執(zhí)行程序的鏡像就被Apphelp替換成了regsvr32.exe ,而啟動的時候命令行上加上想要注入高權(quán)限進(jìn)程的dll文件,就會使得regsvr32.exe在高完整性級別上加載我們想要加載的DLL,James的DLL代碼里實(shí)現(xiàn)的是啟動一個計(jì)算器程序。
這個利用方法引來了一些爭議,很多人認(rèn)為這樣只證明可以繞過UAC默認(rèn)級別下中完整性級別到高完整性級別的彈框,通常來說不能說是安全漏洞,而且實(shí)際之前也有很多公開的技巧可以繞過UAC提示。
但就如James自己說的,UAC的繞過僅僅只是個方便的演示方式,想一想測一測就能發(fā)現(xiàn),這個漏洞還有更多的利用方式。
以IE11 的沙箱(PM)為例,這個漏洞需要的相關(guān)功能(包括BITS Token的獲取、NtAppHelpCacheControl的調(diào)用、sdb Tag的獲取等等)都是可以在IE11沙箱的低完整性級別下執(zhí)行的,那么惡意代碼一旦進(jìn)入沙箱,就可以利用這個漏洞劫持一個常用的中完整性級別程序(甚至系統(tǒng)中經(jīng)常被啟動的高完整性、系統(tǒng)完整性級別程序),就可能通過這個漏洞穿透沙箱,在沙箱外執(zhí)行惡意代碼。使用icacls.exe /setintegritylevel 給測試程序設(shè)置Low完整性級別后,可以很容易證實(shí)這點(diǎn)。
即使是在通過Users用戶組的賬戶登錄的情況下,如果同時或之后有管理員賬戶登錄,由于這個AppHelp Cache是全局跨Session的,因此管理員運(yùn)行的程序也會可能受到劫持的影響,發(fā)生權(quán)限提升。
這些都是通過James的測試程序就能完成的攻擊,但是這個攻擊的一個缺點(diǎn)是必須要有高權(quán)限的程序去啟動, 那么還有更好的利用方式么?
肯定是有的,大家看到剛才我們介紹的AppHelpCacheFlush功能了吧,這個接口的調(diào)用者驗(yàn)證也存在和 AppHelpCacheUpdate同樣的問題,那么通過這個接口就可以直接將內(nèi)存中已經(jīng)添加的AppHelpCache刷入注冊表中(HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerAppCompatCache下AppCompatCache),這樣下次開機(jī)時就會加載并應(yīng)用添加的規(guī)則,這樣重新啟動后就可以更方便的劫持想要劫持的程序。
同時,通過調(diào)用低完整性級別/低權(quán)限賬戶或AppContainer完整性級別下允許調(diào)用的某些服務(wù)LPC/COM接口,也可以使其啟動特定的高權(quán)限程序,并可能進(jìn)行劫持。
這些技術(shù)含量不高,已經(jīng)提供了這么多Tips,再具體的方式就需要大家發(fā)揮想象力了,最后再提點(diǎn)細(xì)節(jié):AppHelp規(guī)則不僅能應(yīng)用于EXE進(jìn)程創(chuàng)建,也可以應(yīng)用在指定的任意模塊加載時。
Windows7的問題
James在說明里提到了Windows7的問題,在Windows7上, AppHelpCacheUpdate這條指令專門被一個特別的ApphelpCacheVerifyContext檢查所保護(hù)(對于其他被保護(hù)接口使用的是ApphelpCacheVerifyAdminContext,和Windows8/8.1一樣存在這個安全漏洞),這個檢查更嚴(yán)格,僅在主TOKEN具備TCB特權(quán)時才可以通過,代碼如下:
NTSTATUS ApphelpCacheVerifyContext()
{
status = STATUS_SUCCESS;
if ( PsGetCurrentThreadPreviousMode() && SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode) == FALSE)
status = STATUS_ACCESS_DENIED;
return status;
}
看上去這個檢查函數(shù)是無法繞過了,James同時提到,這個檢查是在CommandBuffer中的某些Flags匹配時,才會進(jìn)行,也許可以繞過,那么實(shí)際如何呢?
我們逆向NtAppHelpCacheControl在Win7上的相關(guān)實(shí)現(xiàn)可以得知,在CommandBuffer->Flags的Bit 2,3為1時,是不進(jìn)行這個檢查的,這樣的Entry是可以通過AppHelpCacheUpdate的檢查,加入緩存中的。
但是,我們再來看AppHelpCacheLookup的相關(guān)實(shí)現(xiàn)就不難發(fā)現(xiàn), 僅當(dāng)entry->Flags的bit 0為1時,Lookup才為調(diào)用者返回CommandBuffer->DataBuffer和DataBufferSize,而就像在第一節(jié)里提到的,這兩個域描述了APPHELP_LOOKUP_RESULT數(shù)據(jù)結(jié)構(gòu),沒有這個數(shù)據(jù)我們無法指定要應(yīng)用的規(guī)則。
所以至少通過目前的分析來看,通過修改Flags的方法,是無法將達(dá)成我們的劫持?jǐn)?shù)據(jù)加入緩存并生效的目的的,暫時可以認(rèn)為這個漏洞難以在Windows7上實(shí)現(xiàn)利用。