DLL劫持漏洞详解
1.DLL是什么
DLL是Dynamic Link Library的缩写,意为动态链接库。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可有多个DLL文件,一个DLL文件也可能被几个应用程序所共用,这样的DLL文件被称为共享DLL文件。
1.静态调用方式:由编译系统完成对 DLL 的加载和应用程序结束时 DLL 卸载的编码(如还有其它程序使用该 DLL,则 Windows 对 DLL 的应用记录减1,直到所有相关程序都结束对该 DLL 的使用时才释放它,简单实用,但不够灵活,只能满足一般要求。
2.动态调用方式:是由编程者用 API 函数加载和卸载 DLL 来达到调用 DLL 的目的,使用上较复杂,但能更加有效地使用内存,是编制大型应用程序时的重要方式。
2.环境
Windwos Server 2012 |
3.漏洞原因
如果在进程尝试加载一个DLL时没有指定DLL的绝对路径,那么Windows会尝试去指定的目录下查找这个DLL;如果攻击者能够控制其中的某一个目录,并且放一个恶意的DLL文件到这个目录下,这个恶意的DLL便会被进程所加载,从而造成代码执行。这就是所谓的DLL劫持。
DLL劫持漏洞翻译成英文叫做 DLL Hijacking Vulnerability,CWE将其归类为Untrusted Search Path Vulnerability。如果想要去CVE数据库中搜索DLL劫持漏洞案例,搜索这两个关键词即可。可见DLL劫持额主要原因是目录搜索的问题,我们来看一下DLL的搜索目录
在Windows XP SP2
之前,Windows查找DLL的目录以及对应的顺序如下:
- 进程对应的应用程序所在目录;
- 当前目录(Current Directory);
- 系统目录(通过 GetSystemDirectory 获取);
- 16位系统目录;
- Windows目录(通过 GetWindowsDirectory 获取);
- PATH环境变量中的各个目录;
在Windows XP SP2
之后,Windows查找DLL的目录以及对应的顺序(SafeDllSearchMode 默认会被开启),默认注册表为:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
,其键值为1,此时调用顺序如下:
- 进程对应的应用程序所在目录(可理解为程序安装目录比如C:ProgramFilesuTorrent);
- 系统目录(即%windir%system32);
- 16位系统目录(即%windir%system);
- Windows目录(即%windir%);
- 当前目录(运行的某个文件所在目录,比如C:DocumentsandSettingsAdministratorDesktoptest);
- PATH环境变量中的各个目录;
而在Windows7及以上,系统没有了SafeDllSearchMode 而采用KnownDLLs,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用,其注册表位置:在HKLM的如下目录中
SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
Win10中如下:
同样,我们在WIN7的测试系统中也存在这个选项
4.DLL劫持漏洞分析
上文已经写明了DLL漏洞的成因,对dll劫持主要检测目录里的一些调用情况,通过一些监控软件,例如procmon,procmon全称为Process Monitor ,是一款能够实时显示文件系统、注册表与进程活动的高级工具,是微软推荐的一个系统监视工具。它整合了旧的 Sysinternals 工具,Filemon 与 Regmon,并增加了进程ID、用户、进程可靠度、等等监视项,可以记录到文件中。它的强大功能足以使 Process Monitor 成为你系统中的核心组件以及病毒探测工具。通常在病毒分析中会使用这个工具来监控软件的各种行为,同样也可以使用一些其他的行为监控软件来分析软件的行为,从而确定哪个dll可以劫持。
劫持应用中没有的dll
这里dll劫持的选用的是notepad++,使用的版本为6.6.6
打开Process Monitor,设置几个过滤条件后,启动notepad
LoadLibrary
和LoadLibraryEx
一个是本地加载,一个是远程加载,如果DLL不在调用的同一目录下,就可以使用LoadLibrary(L"DLL绝对路径")
加载。但是如果DLL内部又调用一个DLL,就需要使用LoadLibraryEx
进行远程加载,语法如下
LoadLibraryEx(“DLL绝对路径”, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
LoadLibraryEx
的最后一个参数设置为LOAD_WITH_ALTERED_SEARCH_PATH
即可让系统dll搜索顺序从我们设置的目录开始
打开VS2022创建一个动态链接库
// dllmain.cpp : 定义 DLL 应用程序的入口点。 |
编译并复制到NotePad++目录下
启动Notepad++
劫持应用中存在的dll
修改条件为Result is SUCCESS
在动态调用的时候,一般代码通过loadlibrary去加载dll 并作为参数传到到导出函数,这里看一下导入表,发现他这里有一个导出函数
编写dll如下。
// dllmain.cpp : 定义 DLL 应用程序的入口点。 |
编译后放到notepad++目录
运行发现报错,同时并没有弹出计算器
这边我们使用dll进行转发,再生成一个恶意的dll执行代码,代码如下
// dllmain.cpp : 定义 DLL 应用程序的入口点。 |
替换并将原DLL修改为SciLexer_org.dll
即可触发
使用工具进行劫持
使用CFF_EXPLORER、Process Explorer、Windbg来查找加载DLL,
找一个不在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
路径里面的dll进行劫持,因为在这个路径里面的dll是优先加载的,加载之后已经进入内核空间,想要劫持难度很大。
确定DLL后可以直接使用Alternate DLL Analyzer
分析导出函数。
同时使用AheadLib
生成转发DLL的CPP文件即可。
我们可以直接在VS2019中创建动态链接库并加入我们想要执行的命令即可
// dllmain.cpp : 定义 DLL 应用程序的入口点。 |
编译替换运行即可。
部分自动化工具
目前使用效果都不是很好,更多的还是需要手工分析。
https://github.com/anhkgg/anhkgg-tools
https://github.com/sensepost/rattler
5.如何防御
- 在加载DLL时尽量使用DLL的绝对路径;
- 调用SetDllDirectory(L””)把 当前目录 从DLL搜索目录中排除;
- 使用 LoadLibraryEx 加载DLL时,指定 LOADLIBRARY_SEARCH 系列标志;
- 此外,进程也可以尝试去验证DLL的合法性,例如是否具有自家的合法数字签名、是否是合法的系统DLL文件等。