LNK快捷方式文件漏洞

[TOC]

漏洞简介

LNK 快捷方式文件漏洞,又叫 CPL Icon 加载漏洞。用户只要浏览一下 LNK 文件所在的目录病毒就能执行。

漏洞原理及利用分析

既然是快捷方式漏洞,那我们就先了解以下快捷方式文件的格式。

快捷方式文件的格式

快捷方式文件的结构是分段式的。

image-20220721221346481

触发漏洞的数据保存在 Shell Item Id List 段中,所以我们只介绍该段的数据结构。

Shell Item Id List 段为可选段,一个快捷方式文件中是否存在 Shell Item Id List 段是由文件头中偏移 0x14 位置处的值来决定,0bit 值为 1 时,表示该 lnk 文件包含该结构。同时如果存在该结构,偏移 0x4c 的位置的会存在一个 unsigned short int 型的变量,用来标识 Shell Item Id List 结构的大小,紧随其后的为一系列的 SHITEMID 结构,该结构体定义如下。

1
2
3
4
5
6
7
8
typedef struct _SHITEMID
{
unsigned short int cb;	//cb标识一项SHITEMID结构大小
unsigned char abID[0];	//abID是可变结构,
    					//存储具体数据,但第0项里面的
    					//数据是不能修改的,
    					//否则.lnk文件无法运行。
}SHITEMID,*LPSHITEMID;

image-20220721222609971

漏洞触发的原理:SHELL32.DLL 在根据 Shell Item Id 加载快捷方式图标的时候未对被加载的项目(DLL 或者 CPL)进行有效性校验,造成了攻击者可能通过构造特殊的 Shell Item Id 来加载恶意的 DLL。

漏洞文件生成

实验环境:windows 2000 sp4

由于触发这个漏洞需要 CPL 加载机制,所以并不是所有的快捷方式文件都可以触发这个漏洞,只有指向控制面板下面功能的快捷方式才能够出发这个漏洞。

  1. 建立基础 LNK 文件: 大家可以通过右键单击控制面板下面的图片,然后选择“创建快捷方式”选项来建立基础 LNK 文件。在这我们以“鼠标”为例。

image-20220731143316381

  1. 将建立好符合要求的基础 LNK 文件改造成能够触发漏洞的 POC 文件: (1)用WinHex打开鼠标.LNK文件,将偏移 0x7A 到 0x7D 的 0x9CFFFFFF 修改为 0x00000000。需要注意的是,如果您在建立基础 LNK 文件时不是使用的“鼠标”项目, 0x7A 到 0x7D 的值可能不是 0x9CFFFFFF。

image-20220726105411454

​ (2)在偏移 0x8E 位置处写入我们要加载的 DLL 地址,在这我们使用 C:\DLL.DLL,其效果是弹出一个“test”对话框,当然您也可以选择使用其他的 DLL 来进行调试。

image-20220731143558861

漏洞产生原理

这个 POC 的最终目的是加载一个 DLL 文件,所以我们可以先在 LoadLibraryW 函数上设置断点,然后再通过回溯函数调用过程的方式找到上层函数,最终找到出问题的函数。

首先,我们先用下面的源码 再vc6.0中编写一个能弹出对话框的 DLL 文件,然后把这个文件重命名为 DLL.DLL 并放到 C 盘下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//DLL.dll
#include "stdafx.h"

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	MessageBoxA(NULL,"Hello World","Test",MB_OK);
    return TRUE;
}

然后再用 OllyDbg 附加到 explorer.exe 的进程上,对 LoadLibraryW 函数设置断点,接下来浏览以下保存我们修改的 LNK 文件的目录。此时,可能会出现两种情况。

  1. OllyDbg 中断触发。如果是这样我们就能在 view –>Call stack 菜单中查看当前函数调用情况。
  2. OllyDbg 中断未触发,这是因为您已经浏览过这个目录。由于快捷方式图标的加载具有缓存机制,如果以前已经加载过,现在他就不会再重复加载。此时您可以将 LNK 文件重命名,之后 OllyDbg 中的断点就可以触发了。

image-20220731154330734

中断后,我们能在栈中看到本次 LoadLibraryW 函数调用的 FileName 参数为 “C:\DLL.DLL”,说明我们找对地方了,并且我这里也直接出现了弹出框。如果没有看到这个参数,那就多按几次 F9。

接下来我们通过 Call stack 查看函数调用情况。 image-20220731154949928

shell32在处理lnk文件的时候要把它的图标显示出来,对于一般文件应该是从Icon filename string里面解析,但对于文件后面没有Icon filename string结构的控制面板快捷方式,是直接从0x7A那里的iconindex来解析。当这个iconindex是0的时候,会去加载cpl文件,调用CPlApplet接口,这样就存在执行DLL中代码的问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
7D716064    53              push    ebx
7D716065    FF75 10         push    dword ptr [ebp+10]
7D716068    8D5E 0C         lea     ebx, dword ptr [esi+C]
7D71606B    53              push    ebx
7D71606C    FF75 0C         push    dword ptr [ebp+C]      ;C:\dll.dll,0,<-这个0就是从偏移7A那里得到的iconindex
7D71606F    FF15 0015597D   call    dword ptr [<&KERNEL32.lstrcpynW>>; kernel32.lstrcpynW
7D716075    6A 2C           push    2C
7D716077    FF75 0C         push    dword ptr [ebp+C]
7D71607A    FF15 F41B597D   call    dword ptr [<&SHLWAPI.StrChrW>]   ; shlwapi.StrChrW
7D716080    85C0            test    eax, eax
7D716082    74 5D           je      short 7D7160E1
7D716084    66:8320 00      and     word ptr [eax], 0
7D716088    83C0 02         add     eax, 2
7D71608B    50              push    eax
7D71608C    FF15 641C597D   call    dword ptr [<&SHLWAPI.StrToIntW>] ; shlwapi.StrToIntW
7D716092    8B7D 14         mov     edi, dword ptr [ebp+14]
7D716095    8907            mov     dword ptr [edi], eax
7D716097    8B45 18         mov     eax, dword ptr [ebp+18]
7D71609A    C700 02000000   mov     dword ptr [eax], 2
7D7160A0    8B0F            mov     ecx, dword ptr [edi]
7D7160A2    33D2            xor     edx, edx
7D7160A4    3BCA            cmp     ecx, edx      ;偏移0x7A必须得是0,才会LoadLibary
7D7160A6    75 33           jnz     short 7D7160DB
7D7160A8    C700 1A000000   mov     dword ptr [eax], 1A
7D7160AE    8D86 14020000   lea     eax, dword ptr [esi+214]
7D7160B4    3910            cmp     dword ptr [eax], edx
7D7160B6    8955 0C         mov     dword ptr [ebp+C], edx
7D7160B9    75 16           jnz     short 7D7160D1
7D7160BB    8D4D 0C         lea     ecx, dword ptr [ebp+C]
7D7160BE    51              push    ecx
7D7160BF    8D8E 18020000   lea     ecx, dword ptr [esi+218]
7D7160C5    51              push    ecx
7D7160C6    50              push    eax
7D7160C7    53              push    ebx
7D7160C8    E8 48C4F2FF     call    7D642515      ;这里调用后面
 
后面
7D63866D    FF15 6415597D   call    dword ptr [<&KERNEL32.GetCurrent>; kernel32.GetCurrentProcessId
7D638673    50              push    eax
7D638674    56              push    esi
7D638675    68 00001000     push    100000                           ; UNICODE "f1df_6.0.2600.5512_x-ww_35d4ce83\"
7D63867A    8985 E4FDFFFF   mov     dword ptr [ebp-21C], eax
7D638680    FF15 6815597D   call    dword ptr [<&KERNEL32.OpenProces>; kernel32.OpenProcess
7D638686    3BC6            cmp     eax, esi
7D638688    8985 E8FDFFFF   mov     dword ptr [ebp-218], eax
7D63868E    0F84 E5000000   je      7D638779
7D638694    8D85 F4FDFFFF   lea     eax, dword ptr [ebp-20C]
7D63869A    50              push    eax
7D63869B    FF15 A41C597D   call    dword ptr [<&SHLWAPI.PathFileExi>; shlwapi.PathFileExistsW
7D6386A1    85C0            test    eax, eax
7D6386A3    C785 BCFDFFFF 2>mov     dword ptr [ebp-244], 20
7D6386AD    74 14           je      short 7D6386C3
7D6386AF    8D85 F4FDFFFF   lea     eax, dword ptr [ebp-20C]
7D6386B5    89B5 C0FDFFFF   mov     dword ptr [ebp-240], esi
7D6386BB    8985 C4FDFFFF   mov     dword ptr [ebp-23C], eax
7D6386C1    EB 1A           jmp     short 7D6386DD
7D6386C3    C785 C0FDFFFF 0>mov     dword ptr [ebp-240], 8
7D6386CD    899D C4FDFFFF   mov     dword ptr [ebp-23C], ebx
7D6386D3    C785 D0FDFFFF 7>mov     dword ptr [ebp-230], 7B
7D6386DD    8D85 BCFDFFFF   lea     eax, dword ptr [ebp-244]
7D6386E3    50              push    eax
7D6386E4    FF15 6C15597D   call    dword ptr [<&KERNEL32.CreateActC>; kernel32.CreateActCtxW
7D6386EA    83F8 FF         cmp     eax, -1
7D6386ED    8985 F0FDFFFF   mov     dword ptr [ebp-210], eax
7D6386F3    75 06           jnz     short 7D6386FB
7D6386F5    89B5 F0FDFFFF   mov     dword ptr [ebp-210], esi
7D6386FB    8B3D 8C15597D   mov     edi, dword ptr [<&KERNEL32.Activ>; kernel32.ActivateActCtx
7D638701    8D85 ECFDFFFF   lea     eax, dword ptr [ebp-214]
7D638707    50              push    eax
7D638708    FFB5 F0FDFFFF   push    dword ptr [ebp-210]
7D63870E    FFD7            call    edi
7D638710    33F6            xor     esi, esi
7D638712    46              inc     esi
7D638713    56              push    esi
7D638714    56              push    esi
7D638715    56              push    esi
7D638716    53              push    ebx
7D638717    FF15 00F0787D   call    dword ptr [7D78F000]             ; apphelp.ApphelpCheckExe
7D63871D    85C0            test    eax, eax
7D63871F    75 08           jnz     short 7D638729
7D638721    2185 E0FDFFFF   and     dword ptr [ebp-220], eax
7D638727    EB 0D           jmp     short 7D638736
7D638729    53              push    ebx             ;加载DLL
7D63872A    FF15 A015597D   call    dword ptr [<&KERNEL32.LoadLibrar>; kernel32.LoadLibraryW
7D638730    8985 E0FDFFFF   mov     dword ptr [ebp-220], eax         ; dll.01C00000

参考:

LNK快捷方式文件漏洞简要分析

​ 0day2:软件漏洞分析精要