[好文分享]CVE-2017-8464 LNK 漏洞分析及 POC 关键部分

Posted by

作者:启明星辰ADLab

一、背景

微软在6月中旬发布了一个大补丁包(MS17-013),修补了90多个漏洞,其中包括了两个正在被用来发起入侵攻击的高危漏洞(CVE-2017-8464 和 CVE-2017-8543)。启明星辰ADLab已发布《CVE-2017-8543 Windows Search漏洞分析及POC关键部分》,本文我们主要分析 CVE-2017-8464 LNK 漏洞(CNVD-2017-09382,CNNVD-201706-561)。

CVE-2017-8464 是一个 lnk 文件漏洞,这是继2010年黑客通过同为 lnk 文件漏洞的 CVE-2010-2568 漏洞传播震网病毒之后,首次出现的lnk文件漏洞利用程序。与 CVE-2010-2568 相似,CVE-2017-8464 也是利用控制面板快捷方式加载CPL过程中的实现缺陷加载 dll,导致 dllmain 中的代码得到执行。当你的眼睛看到恶意lnk文件的时候,恶意代码就已经通过漏洞获得了执行权限,所以该漏洞的危害还是非常大的。

二、LNK 文件格式和 POC 构造

通过浏览微软的文档,我们得知一个 lnk 文件格式包含5部分,具体结构如下表所示:

其中红色部分为必选部分,蓝色部分的存在与否取决于 SHELL_LINK_HEADER 中的 LinkFlags 字节,绿色部分 EXTRA_DATA 是一个 ExtraData 结构体的数组,数组可以由0到多个 ExtraData 构成。

CVE-2017-8464 这个漏洞和 LINKTARGET_IDLIST 以及 EXTRA_DATA 两项有关,再加上必须出现的 SHELL_LINK_HEADER 就可以构造出一个可以利用的 POC,所以我们这里也只介绍这三部分结构。

SHELL_LINK_HEADER 的格式见下表:

其中 HeaderSize 代表着 SHELL_LINK_HEADER 结构的大小,是一个固定值0x4C,LinkCLSID 的取值也是固定的必须为十六进制的 00021401-0000-0000-C000-000000000046

除了上述两个字段以外,LinkFlags 也是一个很重要的标志位,不同的比特位代表了不同的意义,它决定了 lnk 文件接下来如何解析。微软对于 LinkFlags 的描述如下:

以下截取部分标志位定义:

如果想要触发漏洞的话 lnk 文件需要有 LINKTARGET_IDLIST 结构,也就是说 LinkFlags 的A位需要置位,对其他位则没有特殊要求。

SHELL_LINK_HEADER 中的其他字段和本漏洞的触发没有什么关系,无需填写特定数值。

2. LINKTARGET_IDLIST

LINKTARGET_IDLIST 的格式我们用下图来表示。

2字节的size代表整个 LINKTARGET_IDLIST 的大小 ItemID 是一个用于描述 lnk 文件的目标。

一般情况下对于控制面板的快捷方式而言 ItemID[0] 是固定的。

\x14\x00(代表长度) 
\x1F\x50(代表类型之类的东西) 
\xE0\x4F\xD0\x20\xEA\x3A\x69\x10\xA2\xD8\x08\x00\x2B\x30\x30\x9D 是 GUID 可以从注册表 HKEY_CLASSES_ROOT\CLSID 下寻找对应项,经过查找得知它对应我的电脑。

据 CVE-2010-2568 样本分析的经验,ItemID[1] 填充控制面板的 GUID {21EC2020-3AEA-1069-A2DD-08002B30309D},ItemID[2] 需要填充对应的 CPL 程序路径,同时 ItemID[2] 还要满足几个要求长度大于 0x18,同时 GUID 的开头需要是 xxxxxxxx-0000-0000-006A 的格式,只有这样 windows 才会认定它是一个 UnicodeCPLworker。其验证逻辑如下:

3. EXTRA_DATA

想要触发这次的漏洞仅仅有 LINKTARGET_IDLIST  SHELL_LINK_HEADER 字段还不够,除此以外还需要填充一个 EXTRA_DATAEXTRA_DATA 在微软文档中的定义如下图,其中的 EXTRA_DATA_BLOCK 结构有很多种类型,不同的 BLOCK 结构大相径庭,这里我们主要来介绍与漏洞相关的 BLOCK—SpecialFolderDataBlock(为什么是这种BLOCK我们稍后会提到),我们先来看看这种BLOCK的结构。

其中 BlockSize 是指结构的长度,值是固定值 0x10,BlockSignature 也是一个固定值其值为0xA0000005,Offset 代表 ItemID[2] 在 ItemID 数组中的偏移,由于 ItemID[0] 和 ItemID[1] 的大小都为 0x14,这里 Offset 填 0x28

TERMINAL_BLOCK 由 ExtraDataBlock 数组(可以为0个)和4字节的 TerminalBlock 组成,由于 ExtraDataBlock 与漏洞无关所以不需要它,而 TerminalBlock 根据微软的文档只要小于4即可。

4. POC细节

根据上文分析我们可以尝试构造一个简单的 POC 了(这里只描述重要细节),首先我们创建一个 0x4C 大小的头部空间,并根据微软的文件要求,和我们自己的需求分别对 HeaderSize、LinkCLSID、LinkFlags 进行填充,得到的结构细节如图显示:

红色:HeaderSize 
蓝色:LinkCLSID 
紫色:LinkFlags

接下来是 LINKTARGET_IDLIST 结构的构造,按照我们之前说的细节分别将 ItemID[0]、ItemID[1] 和 ItemID[2] 填充为我的电脑、控制面板和需要加载的CPL程序的路径,如下图:

红色:ItemID[1] 
蓝色:ItemID[2] 
紫色:ItemID[3]

完成 ItemID 结构数组构造之后,还需要在 ItemID 结构数组前面加上 ItemID 结构数组的总长度。最后对 EXTRA_DATA 进行填充,将 BLOCK 设置为 SpecialFolderDataBlock 类型并根据微软的要求构造其结构体,最后得到下图结构:

红色:BlockSize 
蓝色:BlockSignature 
紫色:Offset 
青色:TerminalBlock

三、漏洞分析

这里我们以 windows10 系统为例,来分析 windows 系统对 lnk 文件的处理,其实现位于 windows.storage.dll  CShellLink::_LoadFromStream 函数中。

函数对 SHELL_LINK_HEADER 的验证如下,首先取前4个字节验证其是否为 0x4C,接着读取SHELL_LINK_HEADER剩下的 0x48 个字节,并验证其中的 LinkCLSID 取值是否为 00021401-0000-0000-C000-000000000046

在检测完 SHELL_LINK_HEADER 的 HeaderSize 和 LinkCLSIDWindows 两项之后,windows会查看 LinkFlags 的最低一个 byte 也就是 A 位是否置1,如果置1则代表着这个 lnk 文件中是存在LINKTARGET_IDLIST字段的,就进入CShellLink::_LoadIDList 函数对 LINKTARGET_IDLIST 进行解析。

在我们构造的 poc 中 LinkFlags 的最后一个字节是1,所以 lnk 文件存在 LINKTARGET_IDLIST 字段。

2. LINKTARGET_IDLIST 结构处理

在分析完程序对 SHELL_LINK_HEADER 的处理之后,我们来分析一下程序是如何对 LINKTARGET_IDLIST进行解析的,CShellLink::_LoadIDList 的实现首先读取了2字节的数据,即 IDList 的大小,并用之分配并初始化一片相应大小的内存。

然后读取 LINKTARGET_IDLIST 的数据,并遍历每个 ItemID。

ItemID中的数据包含了一个dll的路径,这个dll稍后将会被加载进内存。

在读取 LINKTARGET_IDLIST 之后 windows 会继续对 LinkFlags 进行验证,通过查看对 LinkFlags 的定义我们知道,这是为了验证 lnk 文件中是否存在 LINKINFO、STRING_DATA,就这个漏洞而言,它们的本身是否存在并不影响最终结果,所以我们继续往下看。

3. Extra_Data 数据解析

与漏洞相关的第三个结构是 ExtraData,也是我们需要重点关注的一个结构,Windows 对 ExtraData 的读取实现位于 shlwapi.dll  SHReadDataBlockList 函数中。

它先读取 BlockSize,再根据size来确定还剩下多少个字节需要读取,将 Extra_Data 读到内存中之后,程序调用 IsValidDataBlock 来验证 ExtraData 的合法性,具体做法是通过 BlockSize 和 BlockSignature 来区分这段数据是不是一个合法的 ExtraData。

在读完 ExtraData 之后程序返回到 CShellLink::_LoadFromStream,执行到CShellLink::_DecodeSpecialFolder函数,该函数会通过 SHFindDataBlock 来判断传入的Extra_Data_Block,如果不存在 KnownFolderDataBlock 和 SpecialFolderDataBlock 则直接返回。

4. 处理 SpecialFolderDataBlock 并加载 DLL

当解析的 EXTRA_DATA_BLOCK 为 SpecialFolderDataBlock 时,会走到如下流程,这里会取出 ExtraData 中的 offset 字段。

并根据该字段去寻找 IDList 中的 ItemID,当找到后进入到 TranslateAliasWithEvent 函数。

TranslateAliasWithEvent 读取 ItemID[2] 中的 dll 路径,并将该路径传给 CPL_LoadCPLModule 函数,该函数会调用 LoadLibraryW,而加载的 dll 路径来自于 lnk 文件中。通过调试我们看到 dll 已经被加载到了内存中。

四、补丁分析

微软在执行 CPL_LoadCPLModule 前多加了一层校验,在 _IsRegisteredCPLApplet 函数中程序会对传入的文件路径进行判断,如果判断失败则不会继续调用 CPL_LoadCPLModule 函数。

五、参考文献

https://msdn.microsoft.com/en-us/library/dd871305.aspx 
http://www.vxjump.net/files/vuln_analysis/cve-2017-8464.txt

https://github.com/nixawk/labs/blob/master/CVE-2017-8464/exploit_CVE-2017-8464.py

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注