CVE-2020-0729:通过.LNK文件进行远程代码执行

在趋势科技漏洞研究服务漏洞报告的摘录中,趋势科技研究团队的John Simpson和Pengsu Cheng详细介绍了Microsoft Windows .LNK文件中最近的远程执行代码错误。以下是其写作的一部分,涵盖了CVE-2020-0729,并做了一些最小的修改。

在微软于2020年2月发布的补丁程序星期二,该公司发布了令人难以置信的99个CVE的安全修复程序,一个月内就修复了相当多的漏洞。尽管已经对被积极利用的Scripting Engine漏洞给予了很多关注,但确实被认为是至关重要的另一个漏洞。CVE-2020-0729被认为是涉及Windows LNK文件(也称为快捷文件)的远程执行代码漏洞。造成此漏洞如此引人注目的部分原因在于,从历史上看,LNK文件中的漏洞利用已被用来传播诸如著名的Stuxnet之类的恶意软件,并且在大多数情况下,仅浏览到包含恶意LNK文件的文件夹,无论本地共享或网络共享足以触发该漏洞。问题就变成了,该漏洞是否具有与过去的某些LNK漏洞相同的利用潜力?由于LNK文件是二进制文件格式,因此仅包含一些顶级结构的文档,因此回答此问题需要大量挖掘。

开始分析

对于Microsoft星期二的补丁程序,这是我们研究团队通过为给定Windows平台打开“仅安全”补丁程序包并开始分析漏洞的标准程序,并根据Microsoft咨询中的信息,尝试在该补丁程序中查找文件可能与漏洞有关。二月补丁包不包含通常与处理LNK文件相关的任何常见DLL(例如shell32.dll和windows.storage.dll)的更新,这使我们无法确定问题的根源。但是,仔细查看文件列表后,一个特殊的DLL对我们来说很突出:StructuredQuery.dll。之所以如此脱颖而出,部分原因是我们过去看到的漏洞已明确命名为涉及StructuredQuery,例如CVE-2018-0825,但在星期二的特定补丁中,没有这样的建议。那么,LNK文件和StructuredQuery之间有什么联系?在Microsoft的Windows Dev Center上快速搜索StructuredQuery,使我们获得了structuredquery.h标头的文档,该标头告诉我们Windows Search使用了它,这正是LNK文件和StructuredQuery连接的地方。

LNK文件的众多人才

LNK文件因包含二进制结构而闻名,该二进制结构创建了文件或文件夹的快捷方式,但是鲜为人知的功能是LNK文件可以直接包含保存的搜索。通常,当用户在Windows 10中搜索文件时,“搜索工具”选项卡会显示在资源管理器功能区中,允许用户优化其搜索并为搜索查询选择高级选项。该选项卡还允许用户保存现有搜索以供以后使用,从而保存带有扩展名“ .search-ms”的XML文件,该文件格式仅部分记录在案。

查看全图
纸飞机sec
但是,这不是保存搜索的唯一方法。如果单击地址栏中的搜索结果图标(将其在下图突出显示)并将其拖到另一个文件夹,它将创建一个LNK文件,该文件包含将在“ search-ms” XML文件中包含的数据的序列化版本。

查看全图
纸飞机安全
考虑到这一点,让我们看一下使用BinDiff的StructuredQuery的补丁差异。

查看全图
纸飞机安全
如我们所见,仅一个功能已更改,StructuredQuery1::ReadPROPVARIANT()并且基于仅81%的相似性,它似乎已进行了相当大的更改。快速比较流程图似乎可以确认变化是相当广泛的:

查看全图
结构化查询-readpropvariant-bindiff.png
该功能在LNK文件的上下文中到底有什么作用?答案需要详细查看保存的搜索LN​​K文件中包含的未记录结构,因此让我们深入了解一下。

Windows Shell链接文件具有一些必需和可选组件,如Shell Link(.LNK)二进制文件格式规范中所定义。每个外壳程序链接文件必须至少包含以下格式的外壳程序链接头:

查看全图
图片5.png
除非另有说明,否则所有多字节字段均以小尾数字节顺序表示。

LinkFlags字段指定是否存在可选结构以及各种选项(例如,shell链接中的字符串是否以Unicode编码)。以下是LinkFlags字段的说明性布局:

查看全图
图片6.png
在大多数情况下设置的一个标志HasLinkTargetIDList由位置“ A”表示,LinkFlags字段的第一个字节的最低有效位。如果设置,LinkTargetIDList结构必须遵循Shell Link Header。LinkTargetIDList结构指定链接的目标,并具有以下布局:

查看全图
Picture7b.png
包含在其中的IDList结构指定持久项ID列表的格式:

查看全图
图片8.png
ItemIDList的用途与文件路径相同,其中每个ItemID结构对应于类似路径的层次结构中的一个路径组件。ItemID可以引用实际的文件系统文件夹,诸如“控制面板”或“保存的搜索”之类的虚拟文件夹,或充当执行特定功能的“快捷方式”的其他形式的嵌入式数据。有关ItemID和ItemIDList的更多常规信息,请参阅Microsoft的Common Explorer Concepts。对漏洞特别重要的是包含存储的搜索查询的LNK文件中存在的ItemIDList和ItemID结构。

当用户创建包含搜索信息的快捷方式时,结果文件将包含一个IDList结构,该结构以Delegate Folder ItemID开头,后跟特定于搜索查询的User Property View ItemID。通常,ItemID从以下结构开始:

查看全图
纸飞机sec
从偏移量0x0004开始的两个字节的值与ItemSize和ItemType结合使用,以帮助确定ItemID的类型。例如,如果ItemSize为0x14,ItemType为0x1F,则检查0x0004处的2个字节以查看其值是否大于ItemSize。如果是这样,则假定剩余的ItemID数据将由一个16字节的全局唯一标识符(GUID)组成。这通常是在LNK文件中找到的第一个ItemID的结构,该文件指向文件或文件夹。如果ItemSize大于包含GUID所需的大小,但小于0x0004处的字节,则将GUID之后的剩余数据视为ExtraDataBlock,以2字节大小字段开头,后跟那么多字节的数据。

对于代表文件夹ItemID,相同的2个字节对​​应于其余结构的大小字段,从而导致以下总体结构:

查看全图
图片10.png
LNK文件中的所有GUID使用GUID 的RPC IDL表示形式存储。RPC IDL表示意味着GUID的前三个段存储为整个段的小端表示(即DWORD后跟2个WORD),而后2个段中的每个字节均视为独立字节。例如,GUID {01234567-1234-ABCD-9876-0123456789AB}具有以下二进制表示形式:

   \x67\x45\x23\x01\x34\x12\xCD\xAB\x98\x76\x01\x23\x45\x67\x89\xAB

委托文件夹ItemID的确切功能未记录。但是,这样的条目可能旨在导致由Item GUID字段指定的类处理后续的ItemID,从而将该类建立为层次结构的根名称空间。如果LNK文件包含嵌入式搜索数据,则项GUID将为{04731B67-D933-450A-90E6-4ACD2E9408FE},对应于CLSID_SearchFolder(对Windows.Storage.Search.dll的引用)。

委托文件夹ItemID后跟一个用户属性视图ItemID,其结构类似于委托文件夹ItemID的结构:

查看全图
图片11.png
此报告特别重要的是PropertyStoreList字段,如果存在的话,它包含一个或多个序列化的PropertyStore项目,每个项目具有以下结构:

查看全图
图片12.png
“属性存储数据”字段是一系列属性。给定PropertyStore中的所有属性都属于Property Format GUID标识的类。每个特定的属性都由称为属性ID或PID的数字ID标识,当与属性格式GUID结合使用时,它们被称为属性键或PKEY。如果属性格式GUID等于,则以稍微不同的方式确定PKEY {D5CDD505-2E9C-101B-9397-08002B2CF9AE}。然后,每个属性都被视为“属性包”的一部分,并具有以下结构:

查看全图
图片13.png
财产袋通常将包含名称为“ Key:FMTID”和“ Key:PID”的元素,标识确定其他元素解释的特定PKEY。特定的“ Property Bag”实施也将要求存在其他元素才能生效。

如果“属性格式” GUID不等于前面提到的“属性袋” GUID,则每个属性均由PID的整数值标识,并具有以下结构:

查看全图
图片14.png
TypedPropertyValue字段对应于Microsoft对象链接和嵌入(OLE)属性集数据结构规范的2.15节中定义的属性集中的属性的类型化值。

Windows SDK随附的标头中定义了各种PKEY。但是,许多文件是未记录的,只能通过检查相关库的调试符号中的引用来识别。对于包含嵌入式搜索数据的LNK文件,用户属性视图ItemID中的第一个PropertyStore具有属性格式GUID,{1E3EE840-BC2B-476C-8237-2ACD1A839B22}该GUID 包含ID为2的属性,该属性对应于PKEY_FilterInfo。

PKEY_FilterInfo的TypedPropertyValue字段由VT_STREAM属性组成。通常,VT_STREAM属性由0x0042类型,2个填充字节和一个IndirectPropertyName组成,后者指定替代流的名称,该替代流包含用于简单属性集存储的PropertySetStream数据包或用于非简单属性集的“ CONTENTS”流元素根据Microsoft 文档的存储。该名称由宽字符串“ prop”指定,后跟与PropertySet数据包中的属性标识符相对应的十进制字符串。但是,由于LNK文件使用嵌入在VT_STREAM属性中的序列化属性存储,因此仅检查IndirectPropertyName以查看其是否以“ prop”开头。该值本身将被忽略。这将导致以下TypedPropertyValue结构:

查看全图
图片15.png
流数据字段的内容取决于流属性所属的特定PKEY。对于PKEY_FilterInfo,流数据实质上包含具有更多序列化PropertyStore结构的嵌入式PropertyStoreList,并且具有以下结构:

查看全图
图片16.png
PKEY_FilterInfo流中的嵌套PropertyStoreList是.search-ms文件中“ conditions”标记的序列化版本。以下是条件标签的示例结构:

查看全图
纸飞机sec团队
该attribute元素的确切功能未公开记录。但是,attribute元素包含对应于GUID的GUID CONDITION_HISTORY和对应于StructuredQuery中的CConditionHistory类的CLSID,这意味着嵌套的条件和属性结构表示搜索查询在保存之前的历史记录。在一般情况下,出现了chs的属性attribute元素确定是否存在任何其他历史记录。当将此结构序列化到属性存储中时,会将其放置在PKEY_FilterInfo PropertyStoreList中,该格式采用带有上述属性格式GUID的属性包的形式。更具体地说,序列化的Conditions结构包含在VT_STREAM属性中,该属性由名称“ Condition”标识。这将导致具有以下结构的PropertyStore项:

查看全图
图片18.png
条件对象通常是“叶子条件”或“复合条件”对象,其中包含许多嵌套对象,这些嵌套对象通常包括一个或多个“叶子条件”对象以及可能的其他“复合条件”对象。这两个条件对象都以以下结构开头:

查看全图
图片19.png
条件GUID将{52F15C89-5A17-48E1-BBCD-46A3F89C7CC2}用于叶条件和{116F8D13-101E-4FA5-84D4-FF8279381935}复合条件。“属性”字段由属性结构组成,其中属性结构的数量由“属性数量”字段定义。每个属性结构都与attribute.search-ms文件中的元素相对应,并以以下结构开头:

查看全图
纸飞机
属性的其余结构取决于AttributeID和CLSID。对于上述CONDITION_HISTORY属性,attributeID设置为{9554087B-CEB6-45AB-99FF-50E8428E860D},CLSID为{C64B9B66-E53D-4C56-B9AE-FEDE4EE95DB1}。其余结构将是具有以下结构的ConditionHistory对象。请注意,这些字段的名称与attributeXML元素的匹配属性相同:

查看全图
图片21.png
如果的值has_nested_condition大于零,则该CONDITION_HISTORY属性将具有一个嵌套的条件对象,该对象本身可能具有具有嵌套条件的嵌套属性,依此类推。

一旦完全读取了包括所有嵌套结构的顶级属性,“复合条件”和“叶条件”结构便开始不同。复合条件的其余结构如下,相对于“属性”字段的末尾有偏移:

查看全图
图片22.png
该numFixedObjects字段确定将立即遵循多少个附加条件(通常是叶子条件)。

叶子条件的其余结构如下,相对于“属性”字段的末尾有偏移:

查看全图
图片23.png
TokenInformationComplete结构的存在取决于是否设置了前面的标志。如果未设置,则不存在该结构,并且下一个标志立即跟随。如果设置,则存在以下结构:

查看全图
图片24.png
总而言之,以下树显示了具有保存的搜索的LNK文件的最简单的结构,为简单起见,删除了不相关的结构:

查看全图
图片25.png
请记住,使用单个“叶状条件”进行搜索会导致最简单的结构。通常,保存的搜索LN​​K文件将从复合条件和许多嵌套结构(包括许多叶条件)开始。

漏洞

现在,我们已经解释了已保存的搜索LN​​K文件的核心结构,我们可以看一下漏洞本身,它在于如何处理“叶子条件”的PropertyVariant字段。

叶子条件的PropertyVariant字段大致对应于PROPVARIANT结构。PropertyVariant结构由2字节类型组成,后跟该类型特定的数据。重要的是要注意,由于Microsoft规范中指定的填充字节通常不存在,因此StructuredQuery似乎具有PROPVARIANT结构的稍微自定义实现。

还需要注意的是,值0x1000或VT_VECTOR与另一种类型结合使用,意味着将有多个指定类型的值。

PropertyVariant字段的解析由我们前面提到的易受攻击的函数来处理StructuredQuery1::ReadPROPVARIANT()。该函数首先读取2字节类型,并检查是否已设置VT_ARRAY(0x2000),原因是StructuredQuery不支持它:

查看全图
图片26.png
然后,该函数检查类型是否为VT_UI4(0x0013),如果不是,则输入switch语句以处理所有其他类型。

漏洞本身在于如何处理类型为VT_VARIANT(0x000C)的PropertyVariant。VT_VARIANT类型通常与VT_VECTOR结合使用,这有效地导致了一系列PropertyVariant结构。换句话说,这就像具有一个数组,其中数组的成员可以是任何数据类型。

当PropertyVariant的类型设置为VT_VARIANT(0x000C)时,将检查完整类型字段以查看是否设置了VT_VECTOR。

查看全图
图片27.png
如果未设置VT_VECTOR,则会为24个字节的缓冲区分配一个调用,CoTaskMemAlloc()并将该缓冲区传递给对的递归调用ReadPROPVARIANT(),以使该缓冲区将被紧随VT_VARIANT字段的属性填充。但是,在将缓冲区传递给之前,不会对其进行初始化(例如,用NULL字节填充)ReadPROPVARIANT()。

如果嵌套属性的类型为VT_CF(0x0047),则该属性旨在包含指向剪贴板数据的指针,ReadPROPVARIANT()对VT_VECTOR执行相同的检查,如果未设置,则尝试将流的后4个字节写入在先前分配的24字节缓冲区中由8字节值指向的位置。

查看全图
图片28.png
由于缓冲区未初始化,因此数据将被写入未定义的存储位置,从而可能导致任意代码执行。在以下异常中以及在explorer.exe上启用了页面堆的情况下,来自WinDBG的部分堆栈跟踪显示了尝试写入的数据:

查看全图
图片29.png
本质上,如果攻击者能够以正确的方式操纵内存布局,以使未初始化的缓冲区包含其控制的值,则他们可以一次将任意4个字节的数据写入所选的内存地址。

结论

如果不提及漏洞的解决方法,就无法完成对已修补漏洞的分析。在这种情况下,解决方案很简单。用NULL字节填充新分配的24字节缓冲区,以确保攻击者无法利用该内存位置以前使用的剩余缓冲区中的数据。微软在二月份发布了他们的补丁。应当指出,Microsoft在3月解决了另一个LNK漏洞,但是3月补丁与该特定错误无关。

特别感谢趋势科技研究团队的John Simpson和Pengsu Cheng提供了此漏洞的详尽分析。有关趋势科技研究服务的概述,请访问http://go.trendmicro.com/tis/

本文为转载文章!我们需要借鉴优秀的作者给出的思路进行逆向思维、可以发现更多有趣的玩法

本文经授权后发布,本文观点不代表立场,转载请联系原作者。
-- 展开阅读全文 --
Cisco BGP 命令和配置参考
« 上一篇 06-02
开发自己的编程语言 —— 完善数字计算
下一篇 » 06-08

发表评论