首页
社区
课程
招聘
[原创] #30天写作挑战#CVE-2020-0729:Windows LNK远程代码执行漏洞分析(一)
发表于: 2020-9-15 14:57 18651

[原创] #30天写作挑战#CVE-2020-0729:Windows LNK远程代码执行漏洞分析(一)

2020-9-15 14:57
18651

备注说明:该漏洞分析内容实在有点多,打算分开发,本次没有附带动态调试。后续会把详细的动态调试部分拿上来,大家先消化下背景知识和静态流程。

Windows与其他OS一样,支持link的使用,link主要用于存储一个位置(或路径)的调用。在UNIX系统中,称为符号链接。在Windows中,这些二进制对象被定义为"shell link",文件名后缀为".LNK"。这是Shell Link Binary File Format的规范,是一种数据对象,其中包含可用于访问另一个数据对象的信息。

Shell link通常用于支持应用程序启动和链接方案,例如对象链接和嵌入(OLE),但也可以由需要存储对目标文件的引用的功能的应用程序使用。

该漏洞主要是由于对LNK文件的错误解析造成,远程攻击者可以诱惑目标用户浏览包含特制LNK文件的文件夹或者下载一个LNK文件。成功的利用可以实现在当前用户的安全上下文中进行任意代码执行。

• Microsoft Windows 7 SP1<br>
• Microsoft Windows 8.1<br>
• Microsoft Windows 10(1607-1909)<br>
• Microsoft Windows RT 8.1<br>
• Microsoft Windows Server 2008 SP2<br>
• Microsoft Windows Server 2008 R2 SP1<br>
• Microsoft Windows Server 2012<br>
• Microsoft Windows Server 2012 R2<br>
• Microsoft Windows Server 2016<br>
• Microsoft Windows Server 2019<br>
• Microsoft Windows Server version 1803 (Server Core Installation)<br>
• Microsoft Windows Server version 1903 (Server Core Installation)<br>• Microsoft Windows Server version 1909 (Server Core Installation)<br>

微软官方针对该漏洞已发布安全更新补丁,补丁地址:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0729

靶机:Windows 10 1803 x64

靶机操作:在任意位置创建文件夹,将poc文件放入文件夹中

直接双击打开包含poc文件的文件夹(无需双击poc文件,只需双击包含poc的文件夹即可),explorer.exe崩溃重启。

(限于篇幅问题,此处不对.LNK的完全结构做详细记录,而只是介绍一个合法的最小化的结构,感兴趣的同学可以到参考文献中的链接1进行详细的结构学习。结构介绍中所有图片均以BaiduNetdisk.exe的link文件为样例。)

LNK格式可以表达如下:

SHELL_LINK_HEADER:一个ShellLinkHeader结构,包含了确认信息,时间戳,以及指明一些可选结构是否存在的flags。

LINKTARGET_IDLIST:一个可选的LinkTargetIDList结构,指定了link的target。

LINKINFO:一个可选的LinkInfo结构,指明了处理link target必需的信息。

STRING_DATA:0个或多个StringData结构,用于传递用户接口和路径标识信息。

EXTRA_DATA:0个或多个和ExtraData结构。

备注:

ShellLinkHeader是每个shell link文件必须包含的结构,其结构如下:

以上所有结构除特殊说明外,均为小端序。

LinkFlags字段指定了是否存在可选结构以及各种选项,例如shell link中的字符串是否以Unicode编码,其结构如下:

在大多数情况下设置的一个标志HasLinkTargetIDList由位置“ A”表示,即LinkFlags字段的第一个字节的最低有效位,如果已设置,则LinkTargetIDList结构必须紧跟Shell Link Header。一个例子的该字段的详细结构如下:

LinkTargetIDList结构指定了link的target,其结构如下:

如上图所示,首先是IDListSize结构,然后跟了6个ItemID结构(详细结构稍后介绍)的IDListIDList数组指定了一个持久化的item ID list的结构:

ItemIDList的作用实际上与文件路径的作用相同,其中的每个ItemID结构对应于类似路径的层次结构中的一个路径组件。ItemID可以引用实际的文件系统文件夹,例如“控制面板”或“保存的搜索”之类的虚拟文件夹,或充当执行特定功能的“快捷方式”的其他形式的嵌入式数据。

通常情况下,ItemID的结构如下所示:

从偏移量0x0004开始的两个字节的值与ItemSizeItemType结合使用来帮助确定ItemID的类型。例如,如果ItemSize的值为0x14,ItemType的值为0x1f,在0x0004的2个字节的值如果大于ItemSize,那么就认为ItemID的剩余部分由一个16字节的GUID组成,这通常是LNK文件中指向标准文件的第一个ItemID的结构;如果ItemSize的值大于包含GUID所需的值,但是小于0x0004偏移处的值,那么在GUID的后面会跟 一个ExtraDataBlock,block开始为一个2字节的size,后续包含VersionSignature

更多关于ItemIDListItemID的详细信息可以参见参考文献中的ItemID部分。下面的示意图可以更好地理解以上的完整结构:

从图中可以明显看出各种结构的具体的值,与前面的结构相符合。比如Header中的GUID值为{00021401-0000-0000-C000-000000000046}LinkFlags字段的HasLinkTargetIDList设置为1,所以在Header后面紧跟了多个LinkTargetIDList结构,每个结构指向一个target,第一个结构的Value为MyComputer,往后依次为D:\Program File(x86)等等,最后一个为BaiduNetdisk.exe,而所有的LinkTargetIDList组合起来,为该LNK文件指向的实际的application的绝对路径。而第一个LinkTargetIDList时一个虚拟的文件系统文件夹,后面的均为实际的文件系统文件夹。在所有LinkTargetIDList结构的末尾是一个2字节大小的TerminalID,其值为0。

Windows允许用户以2种方式来保存或创建搜索查询的快捷方式。第一种方式是将查询保存为一个XML文件,扩展为".search-ms",该种格式只有一部分内容以文档形式进行了公开。第二种方式是将".search-ms"文件进行序列化然后直接嵌入到LNK文件中,最终的文件包含一个IDList结构,该结构以一个Delegate Folder ItemID开始,后面跟一个User Property View ItemID用于指定搜索查询。

第一种方式:

而打开保存的文件内容可以看到都是xml元素:

第二种方式:

点击前面的搜索图标,会显示保存为一个.search-ms文件,然后按住红框中的图标,直接拖拽到一个路径下,会创建一个LNK文件。而该LNK文件的作用与第一种方式的作用相同,都保存了一样的搜索操作。

查看文件内容,是一个标准的LNK文件:

Delegate Folder ItemID的0x0004偏移处的2个字节是一个表示其余结构的大小的字段,总体结构如下:

LNK文件中的所有GUID均以RPC IDL形式进行存储,即GUID的前3个段均以小端序存储(例如DWORD,WORD等),后2个段均以独立字节存储。例如,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中的详细函数并没有进行公开,但其作用大概是在GUID字段中指定一个class,对后续的ItemID都使用GUID指定的class来进行处理,以这种方式以该class为层次结构的"root"。对于包含嵌入式数据的LNK文件来说,Item GUID{04731B67-D933-450A-90E6-4ACD2E9408FE},对应于CLSID_SearchFolder,即对Windows.Storage.Search.dll的引用。

Delegate Folder ItemID后面跟一个User Property View ItemID,其结构如下:

在以上字段中,与漏洞关系比较大的是PropertyStoreList字段。该字段如果存在,那么会包含一个或多个序列化的PropertyStore iterm,其结构如下所示:

Property Store Data字段是序列化property store中的一系列properties,在PropertyStore中的所有的properties均属于Property Format GUID标识的class。每个property通过一个数字ID进行标识,当与Property Format GUID结合使用时,该ID称为property key或者PKEY

如果Property Format GUID等于{D5CDD505-2E9C-101B-9397-08002B2CF9AE},那么PKEY的确定方式略有不同。每个property当作Property Bag的一部分,格式如下:

Property bags通常包含类似名称为"Key:FMTID"或"Key:PID"的内容,这些名称表示了解释其他元素的特定的PKEY

如果Property Format GUID不是前面提到的Property Bags形式的GUID,那么每个property将会使用一个整数形式的PID进行标识,其结构如下:

TypePropertyvalue对应于property集中的property的类型值,property集遵循 Microsoft Object Linking and Embedding (OLE) Property Set Data Structures 中的规范(详见参考文献3)。

各种PKEY由Windows SDK中的头文件进行定义,但是,许多文件没有记录,只能通过检查相关库的调试符号中的引用来识别。对于包含嵌入式搜索数据的LNK文件来说,User Property View ItemID中的第一个PropertyStore有一个Property Format GUID ,其值为{1E3EE840-BC2B-476C-8237-2ACD1A839B22},其结构中的Id值为2,对应于PKEY_FilterInfo

PKEY_FilterInfoTypedPropertyValue字段包含一个VT_STREAM属性。通常情况下,一个VT_STREAM property包含的type值为0x0042,2字节的padding,和一个IndirectPropertyNameIndirectPropertyName指明了一个备用流,该备用流或者包含simple property set存储中的PropertySetStream数据包,或者包含non-simple property set存储中的CONTENTS流数据(详细内容参考文献4)。该名称由宽字符的字符串"prop"开始,其后跟对应于PropertySet数据包中的property标识符的十进制字符串。

然而,因为LNK文件使用嵌入在VT_SREAM中的序列化property store,IndirectPropertyName仅仅检查是否以"prop"开头,而value本身会被忽略,这就导致TypedPropertyValue的结构变成如下形式:

Stream Data中的数据取决于stream property属于的特定的PKEY。对于PKEY_FilterInfo来说,Stream Data通常包含一个嵌入的PropertyStoreList,其中包含了多个序列化的PropertyStore结构。此时,Stream Data的结构如下所示:

PKEY_FilterInfo stream 中的嵌套PropertyStoreList实际上是.search-ms文件中"conditions"标记的序列化版本。下面是一个conditions tag的样例结构:

"attribute"标签的详细功能尚未公开,但是该标记包含一个对应于CONDITION_HISTORYGUID,一个对应于StructuredQueryCConditionHistory类的CLSID,这表示嵌套的condition和attribute结构代表着之前已保存的搜索查询记录。通常情况下,attribute标签的chs可以确定可选history是否存在(猜测)。当此结构序列化到property store中时,会将其放入
PKEY_FilterInfo PropertyStoreList中,它采用带有上述Property Format GUID的property bag的形式。特别情况下,序列化的Conditions结构包含在一个使用名称为Condition进行标识的VT_STREAM Property中。此时,PropertyStore item的结构如下:

Condition object通常是一个Leaf Condition或者Compound Condition对象,其中可以包含嵌套对象,通常包含一个或多个Leaf Condition,也可能包含其他的Compound Condition对象。2种condition object都具有以下结构:

对于Leaf Condition来说,其Conditioin GUID{52F15C89-5A17-48E1-BBCD-46A3F89C7CC2};对于Compound Condition来说,其Conditioin GUID{116F8D13-101E-4FA5-84D4-FF8279381935}Attributes字段由几个Attributes attribute结构组成,每个对应于之前提到的search-ms文件中的attribute标签,字段结构如下:

Attribute的其他结构取决于AttributeIDCLSID。对于前面的attributeID{9554087B-CEB6-45AB-99FF-50E8428E860D}CLSID{C64B9B66-E53D-4C56-B9AE-FEDE4EE95DB1}CONDIOTION_HISTORY attribute来说,其剩余结构为一个ConditionHistory对象,该对象结构如下:

如果nested_condition的值大于0,CONDITION_HISTORY就会有嵌套的condition object,该对象本身也可能具有具有嵌套条件的嵌套属性,依此类推。

在完全读取了所有的嵌套结构后,Compoound ConditionLeaf Condition结构后续就不同了。紧接Attributes字段后面,Compound Condition的结构如下:

numFixedObjects字段指明了后续会跟多少附加conditions(通常为Leaf Conditions)。

而对于Leaf Condition,其结构如下:

TokenInfomationComplete结构存在与否取决于前面的flag是否进行了设置,如果没有设置那么紧跟下一个flag;如果进行了设置,那么紧跟的结构如下:

对于Leaf ConditionPropertyVariant字段,其结构大致与PROPVARIANT结构(参考文献5)相似。PropertyVariant结构包含2字节的type,其后为该type的data。重要的是要注意,由于参考文献5中指定的填充字节往往不存在,因此StructuredQuery似乎对PROPVARIANT结构进行了自定义实现。 还需要注意的是,值0x1000或VT_VECTOR与另一种类型结合使用,意味着将有多个指定类型的值。

简述一个具有保存的搜哦的LNK文件的最简结构如下所示(去除无关结构):


以上只是一个最简单的使用单个Leaf Condition结构的搜索LNK结构,真实情况种往往是从Compund Condition开始,别切包含很多嵌套结构(包括许多的Leaf Condition)。

造成该漏洞的主要原因是在处理Leaf Condition中的type为VT_VARIANTPropertyVariant结构时发生错误导致。

如果一个LNK文件有一个ItemID List,且其第一个ItemIDDelegate Folder ItemID,且该Delegate Folder ItemIDd的GUID表示CLSID_SearchFolder,那么首先调用Windows.Storage.Search.dll中的CDBFolder::BindToObject()函数进行处理,该函数会调用CDBFolder::GetFilterConditionForChild()函数从child ItemID中获取search filter condition。

第一阶段,CDBFolder::GetFilterConditionForChild()函数在进行查找时,会在child ItemID中搜索PKEY_FilterInfo property,如果找到了,在序列化property store中的包含一个property bag的VT_STREAM就会通过调用SHLoadFilterFromStream()函数进行加载,该函数会创建一个CFilterCondition对象然后提供给IUnknown_LoadFromStream()函数。该操作会调用CFIlterCondition::Load()函数,而该函数会首先将property bag复制到一个内存中的store中,然后开始校验该store的结构,通过查找名称为Name, Type,key:FMTID,Key:PID以及Condition的字符串来确认是否property bag元素。

第二阶段,在确认完所有元素后,调用LoadConditionFromStream()函数来读取Condition property bag中的VT_STREAM,该元素调用IUnknown_LoadKnownImplFromStream()来读取Condition GUID并从StructuredQuery.dll中创建并加载相关的condition对象。在从stream中加载condition对象的过程中,所有嵌套的对象会按照出现的顺序进行加载,包括AttributesConditions。当遇到一个Leaf Condition时,会调用StructuredQuery1::LeafCondition::Load()函数来读取所有的Attributes,然后读取Condition Type PKEYCondition Operation,然后调用StructuredQuery1::ReadPROPVARIANT()函数来读取PropertyVariant结构。

第三节阶段,StructuredQuery1::ReadPROPVARIANT()先读取2字节的type,然后检查VT_ARRAY(0x2000)是否进行了设置,这里主要是因为StructuredQuery不支持。然后进入到一个switch语句来根据type进行不同的处理。如果type设置为VT_VARIANT(0x000c),就会检查完整的type字段来确认是否设置了VT_VECTOR,如果没有设置,则调用CoTaskMemAlloc()来分配包含另一个PropertyVariant结构的24字节的缓冲区。在递归调用StructuredQuery1::ReadPROPVARIANT()函数以读取紧随VT_VARIANT 的type字段之后的另一个PropertyVariant结构之前,并没有对这个缓冲区进行初始化。如果下一个type字段设置为VT_CF(0x0047),则应该是一个包含一个指向剪贴板数据的指针的PropertyVariant结构,ReadPROPVARIANT()函数尝试将stream的后4个字节写入先前分配的24字节缓冲区中的8字节值所指向的位置。由于缓冲区并没有进行初始化,数据会被写入到一个未定义的位置,最终导致任意代码执行。

用一个简单的流程图来描述上面的流程:

(未完待续)
大家稍等哈,最近有活动,实在是忙不开,到时候我看看直接把震网的也一起加进来,抱歉

 
 
SHELL_LINK = SHELL_LINK_HEADER [LINKTARGET_IDLIST] [LINKINFO]
              [STRING_DATA] *EXTRA_DATA
SHELL_LINK = SHELL_LINK_HEADER [LINKTARGET_IDLIST] [LINKINFO]
              [STRING_DATA] *EXTRA_DATA
 
 
 
 
 
 
 
 
                  1                  2                    3
0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|0|0|0|0|0|
                                                |A|
                  1                  2                    3
0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|0|0|0|0|0|
                                                |A|
 
 
 
 
 
 
 
 
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2020-9-27 20:17 被有毒编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (4)
雪    币: 83
活跃值: (1087)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
2
好啊
2020-9-16 18:00
0
雪    币: 207
活跃值: (24)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
MARK一下
2020-9-21 10:06
0
雪    币: 202
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
请教一下,你这个打开lnk文件的程序是什么?
2020-10-28 17:56
0
雪    币: 15187
活跃值: (16852)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
5
abcteeny 请教一下,你这个打开lnk文件的程序是什么?
我用的010editor
2020-10-28 19:57
0
游客
登录 | 注册 方可回帖
返回
//