您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何進行LNK文件中的遠程命令執行漏洞CVE-2020-0729分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
下文是對其中關于CVE-2020-0729部分的摘錄,有一些小的修改。
微軟在2020年2月的“周二補丁日(Patch Tuesday)”上發布了99個CVE的安全補丁,一個月修復這么多漏洞實在是難以置信。由于利用廣泛,很多人都在關注其中的Scripting Engine漏洞,但是還有一個“高危”漏洞也十分重要——CVE-2020-0729,這是一個Windows LNK文件(即快捷方式)中的RCE漏洞。這個漏洞之所以引人注目有一部分歷史原因,以前LNK文件中的漏洞通常被用來傳播惡意軟件,例如著名的Stuxnet,并且大多數情況下,只要瀏覽包含該惡意LNK文件的文件夾就足以觸發該漏洞,無論是在本地還是在網絡共享中。所以問題就變成了,這個RCE漏洞還可以像之前的LNK文件漏洞一樣被這樣觸發嗎?由于LNK文件是二進制的格式,而且只包含一些頂級結構信息,回答這個問題需要更多的研究。
對于我們這些研究團隊來說,微軟的“周二補丁日”意味著我們要開始研究漏洞了,一般要解壓給定Windows平臺的純安全(security only)補丁包,并根據微軟的建議信息,嘗試定位補丁中與漏洞有關的文件。2月份的補丁包里沒有包含與處理LNK文件有關的常用DLL文件的更新,例如shell32.dll和windows.storage.dll,這就讓我們很難找到問題在哪兒。但是,通過對文件列表的仔細檢查,我們發現了一個特殊的DLL文件:StructuredQuery.dll。這個DLL文件之所以特殊,部分原因是因為我們之前看到過涉及到StructuredQuery的正式漏洞,例如 CVE-2018-0825,只是這次的周二補丁中沒有類似的建議。所以LNK文件和StructuredQuery之間有什么關系呢?我們在微軟的開發者中心對StructuredQuery進行了搜索,并找到了一份關于頭文件structuredquery.h的文檔,該文檔提到這個頭文件被用于Windows Search,而這正是LNK文件與StructuredQuery之間的聯系。
眾所周知,LNK文件中包含為文件或文件夾創造快捷方式的二進制結構,但很少有人知道,LNK文件還可以直接包含搜索結果。通常情況下,當用戶在Windows 10中搜索文件時,資源管理器功能區會出現一個“Search”選項卡,用戶可以在這里優化搜索功能,設置搜索的高級選項,還可以保存當前的搜索結果以供之后使用。該搜索結果是一個XML文件,擴展名為".search-ms",對于這種格式的文件,只有一個簡單的文檔介紹。
除了以這種方式保存搜索結果外,如果你將下圖中地址欄上的搜索結果圖標拖動到另一個文件夾中,會創建一個LNK文件,該文件包含"search-ms" XML文件中數據的序列化結果。
知道了這種方法后,我們用BinDiff看一下補丁中StructuredQuery的變化。
可以看到,只有一個函數有變化——StructuredQuery1::ReadPROPVARIANT()。由于相似度只有81%,函數的改變可以說相當大,對兩者的流程圖進行對比可以證實這一點:
所以說這個函數在LNK文件中做了什么呢?這就需要對上面提到的LNK文件中的search-ms文件結構進行一番深入研究了。
根據Shell Link(.LNK)二進制文件格式規范,Windows shell link文件包含幾個必要組件和幾個可選組件,每個shell link文件至少需要包含一個Shell Link Header,其格式如下:
除非另有說明,所有的多字節字段都使用小端模式表示。
LinkFlags字段用來設置是否存在可選結構以及其他各種選項,例如shell link文件中的字符串是否使用Unicode編碼。下面是LinkFlags字段的結構分布:
有一個標志在大多數情況下都會被設置——HasLinkTargetIDList,在圖中用位置"A"表示,即LinkFlags字段中第一個字節的最低有效位。如果設置了該標志,在Shell Link Header之后必須跟隨一個LinkTargetIDList結構,該結構定義了鏈接的目標,并具有如下結構:
其中,IDList結構中包含了一個item ID列表。
ItemIDList的功能與文件路徑類似,其中每個ItemID對應于路徑結構中的一項,它可以代表文件系統中真實的文件夾,或者類似“控制面板”或者“保存的搜索”之類的虛擬文件夾,或者其他形式的嵌入式數據,作為“快捷方式”來執行特定的功能。想了解關于ItemID和ItemIDList的更多信息,請參閱微軟的Common Explorer Concepts)。對于這次的漏洞來說,重要的是包含有搜索結果的LNK文件中ItemIDList和ItemID的結構。
當用戶創建了一個存有搜索結果的快捷方式時,這個快捷方式會包含一個IDList結構,該結構以Delegate Folder ItemID開頭,后面跟有一個和搜索有關的User Property View ItemID。通常來說,ItemID以下面的結構開頭:
從偏移0x0004開始的兩個字節與ItemSize和ItemType一起用于確定ItemID的類型。例如,如果ItemSize是0x14,ItemType是0x1F,偏移0x0004處的兩個字節大于ItemSize,則可以認為ItemID的剩余部分是一個16字節的全局唯一標識符(GUID),LNK文件中的第一個ItemID通常是這種結構,指向一個文件或文件夾。如果ItemSize大于包含GUID所需大小,但是小于偏移0x0004處的兩個字節,則稱GUID后面的剩余數據為ExtraDataBlock,這段數據的前兩個字節是一個大小字段,定義了之后數據的字節數。
對于Delegate Folder ItemID來說,該位置也有一個2字節的大小字段,代表剩余結構的字節數。它具有以下結構:
LNK文件中所有的GUID都以RPC IDL的形式存儲,即GUID的前三個字段中,每個字段都是一個整體,以小端模式存儲(可以看做是一個DWORD后面跟兩個WORD),后兩個字段中的每個字節都是獨立的,舉例來說,GUID {01234567-1234-ABCD-9876-0123456789AB}可以用以下的二進制表示:
\x67\x45\x23\x01\x34\x12\xCD\xAB\x98\x76\x01\x23\x45\x67\x89\xAB
并沒有文檔記錄Delegate Folder ItemID的具體作用,但是該項中Item GUID字段指定的類很可能是用來處理接下來的ItemID的,因此整個路徑結構的根命名空間就是這個由Item GUID指定的類。如果LNK文件中包含搜索結果,則Item GUID為{04731B67-D933-450A-90E6-4ACD2E9408FE},代表CLSID_SearchFolder,是對Windows.Storage.Search.dll文件的引用。
User Property View ItemID跟在Delegate Folder ItemID的后面,其結構與Delegate Folder ItemID類似:
其中的PropertyStoreList字段很重要,該字段中包含一個或多個序列化PropertyStore項,每項都具有以下結構:
Property Store Data字段由一系列屬性組成,這些屬性都屬于Property Format GUID字段指定的類,每個屬性都由一個數字ID標識,即屬性ID或者叫PID。PID和Property Format GUID組合在一起就是屬性鍵,即PKEY。如果Property Format GUID字段等于{D5CDD505-2E9C-101B-9397-08002B2CF9AE},那么PKEY的確定方式會稍有不同,且每個屬性都會成為“屬性包(Property Bag)”的一部分,具有以下結構:
屬性包中一些元素的Name字段格式為Key:FMTID或者Key:PID,這些元素就確定了剩余元素的PKEY,這種情況下,就必須按照順序排列屬性包中的其他元素以使其生效。
如果Property Format GUID字段不等于我們上面提到的GUID值({D5CDD505-2E9C-101B-9397-08002B2CF9AE}),那么每個屬性就由整數值PID標識,該屬性具有以下結構:
其中TypedPropertyValue字段表示屬性集中一個屬性的類型值,具體可參考Microsoft Object Linking and Embedding (OLE) Property Set Data Structures規范第2.15小結。
在Windows SDK的頭文件中定義了很多不同的PKEY,但是很多PKEY都沒有記錄,只能通過檢查相關庫文件的調試符號中的引用來識別。對于包含搜索結果的LNK文件來說,User Property View ItemID中第一個PropertyStore中的Property Format GUID字段為{1E3EE840-BC2B-476C-8237-2ACD1A839B22},包含一個ID值為2的屬性,對應于PKEY_FilterInfo。
PKEY_FilterInfo中的TypedPropertyValue結構中,type字段可選值中存在一個VT_STREAM,使用這個值表示TypedPropertyValue結構由0x0042的type值,兩個填充字節,以及一個IndirectPropertyName組成。IndirectPropertyName指一個可選流數據的名稱,該可選流數據可能包含用于簡單屬性集存儲的PropertySetStream包,或者包含用于非簡單屬性集存儲的"CONTENTS"流元素,具體參考微軟文檔。IndirectPropertyName由寬字符串"prop"開頭,后面跟一個十進制字符串,該十進制字符串是PropertySet包中的屬性標識符,因為LNK文件使用的是序列化后VT_STREAM類型的屬性,所以在查看IndirectPropertyName的時候,只檢查它是否以"prop"開頭,后面的值被忽略。這種情況下,TypedPropertyValue的結構如下:
Stream Data字段的內容取決于該流屬性的PKEY值,對于PKEY_FilterInfo來說,Stream Data包含一個PropertyStoreList,其中又包含更多的序列化PropertyStore,其結構如下:
PKEY_FilterInfo中的PropertyStoreList是.search-ms文件中"conditions"標簽的序列化結果,“conditions”標簽的結構如下:
attribute標簽的確切功能并沒有記錄,但是attribute標簽中包含一個對應CONDITION_HISTORY的GUID,以及一個對應StructuredQuery中CConditionHistory類的CLSID,這就意味著,這種嵌套的condition和attribute結構可能表示搜索結果的歷史記錄,而attribute標簽中的chs屬性表示是否存在任何其他的歷史記錄。對上面這個結構進行序列化,并表示為一個PropertyStore,該PropertyStore放入PKEY_FilterInfo的PropertyStoreList中,這個PropertyStoreList會成為一個屬性包,其中屬性的Property Format GUID就是我們上面提到過的{D5CDD505-2E9C-101B-9397-08002B2CF9AE}。更具體地說,序列化后的Conditions結構包含在一個VT_STREAM類型的屬性中,該屬性由name字段的“Condition”來標識。綜上,我們得到了一個結構如下的PropertyStore項:
其中,Condition object通常是一個“Leaf Condition”或者包含多個嵌套對象的“Compound Condition”,其中的嵌套對象可以是一個或多個Leaf Condition或者其他Compound Condition。這兩種condition object都以下面的結構開頭:
其中,Leaf Condition的Condition GUID為{52F15C89-5A17-48E1-BBCD-46A3F89C7CC2},Compound Condition的Condition GUID為{116F8D13-101E-4FA5-84D4-FF8279381935}。Attributes字段由attribute結構組成,attribute結構的數量由Number of Attributes字段決定,每個attribute結構對應于.search-ms文件中的一個attribute標簽,以如下結構開頭:
Attribute的剩余結構由AttributeID和Attribute CLSID決定。如果是上面提到過的CONDITION_HISTORY attribute,那么AttributeID字段設為{9554087B-CEB6-45AB-99FF-50E8428E860D},Attribute CLSID字段設為{C64B9B66-E53D-4C56-B9AE-FEDE4EE95DB1},剩余結構是一個具有以下結構的ConditionHistory對象,注意其中的字段名稱與XML中attribute標簽的屬性名稱相同:
如果字段has_nested_condition的值大于0,那么CONDITION_HISTORY attribute就有一個嵌套的Conditon object,而這個Conditon object本身也有可能有一個嵌套的Conditon object……
在讀取完頂層的Attributes及其所有嵌套結構后,Compound Condition和Leaf Condition的結構開始不同,Compound Condition的剩余結構如下,其中的offset是相對Attributes字段結尾的偏移:
numFixedObjects字段決定了后面還跟有多少condition(通常指Leaf Condition)。
Leaf Condition的剩余結構如下,其中的offset是相對Attributes字段結尾的偏移:
其中,TokenInformationComplete字段是否出現取決于是否設置了上面對應的TokenInformationComplete flag,如果未設置,則該字段不存在,后面緊跟著下一個TokenInformationComplete flag,如果設置了,則緊接如下結構:
綜上,下圖顯示了一個存有搜索結果的LNK文件可能的最簡結構,簡單起見,所有不相關結構都被刪掉了:
只包含一個Leaf Condition的搜索結果就具有上面這種最簡結構,但大多數情況下,存有搜索結果的LNK文件內會有一個Compound Condition以及許多包含Leaf Condition的嵌套結構。
既然我們已經了解了存有搜索結果的LNK文件的核心結構,那么現在可以關注漏洞本身了,漏洞在于如何處理Leaf Condition的PropertyVariant字段。
Leaf Condition的PropertyVariant字段基本上是一個PROPVARIANT結構,該結構由一個2字節的類型以及屬于該特定類型的數據組成。需要注意的是,StructuredQuery中的PROPVARIANT結構有一些不同,通常沒有Microsoft規范中定義的填充字節。
還有一點需要注意,如果其中的2字節類型值是0x1000 (VT_VECTOR)與另一個類型值的組合,那么結構中會有多個該特定類型的值。
PropertyVariant字段的解析是由我們之前提到的有問題的函數StructuredQuery1::ReadPROPVARIANT()完成的。該函數首先讀入2字節的類型值,檢查該值中是否設置了VT_ARRAY位(0x2000),因為StructuredQuery并不支持該選項:
之后函數會檢查類型是否是VT_UI4(0x0013),如果不是,進入switch語句處理所有其他類型。
漏洞在于如何處理類型為VT_VARIANT(0x000C)的PropertyVariant。VT_VARIANT類型通常和VT_VECTOR一起使用,形成一系列的PropertyVariant結構。換句話說,這種類型就像是一個數組,數組中的每個元素可以是任何數據類型。
如果PropertyVariant的類型被設置為VT_VARIANT(0x000C),類型檢查時會檢查VT_VECTOR位是否設置。
如果沒有設置VT_VECTOR位,ReadPROPVARIANT()函數會通過調用CoTaskMemAlloc()分配一個24字節的緩沖區,緩沖區會傳遞到ReadPROPVARIANT()的遞歸調用中,目的是想將緊跟在VT_VARIANT字段后的屬性填充進緩沖區。但是在傳遞到ReadPROPVARIANT()之前,緩沖區并沒有進行初始化(例如將其充滿空字節)。
如果上面這個嵌套的屬性類型為VT_CF(0X0047),這類屬性包含一個指向剪貼板數據的指針,ReadPROPVARIANT()同樣會檢查VT_VECTOR位,如果沒有設置,函數會嘗試寫入流中接下來的4個字節,寫入位置由之前分配的24字節緩沖區中的一個8字節值指定。
由于緩沖區沒有被初始化,數據會被寫入未定義的內存區域,從而可能導致任意代碼執行。下圖中,WinDBG(啟用Page Heap)顯示的異常以及堆棧跟蹤情況表明程序嘗試進行數據寫入:
如果攻擊者可以正確地操控內存分布,讓未初始化緩沖區包含攻擊者控制的數值,那么他們就可以向該數值指向的內存區域一次性寫入任意4字節數據。
不給出解決方案的漏洞分析是不完整的,對于這次的漏洞,解決方案很簡單,將分配的24字節緩沖區初始化為空字節,確保攻擊者無法利用該內存位置之前留下的數據作為緩沖區內容。微軟在二月份發布了他們的補丁,需要指出的是,三月份的時候微軟又修復了另一個LNK漏洞,但是三月份的這個漏洞與本漏洞沒有關系。
看完上述內容,你們對如何進行LNK文件中的遠程命令執行漏洞CVE-2020-0729分析有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。