Windows DLL劫持漏洞挖掘与利用小记 2021-08-08 14:24:21 Steven Xeldax > 本篇记录一下DLL劫持漏洞的学习过程,之前一直觉得dll劫持漏洞和jsonp属于同一等级很鸡肋但在红队工作中慢慢发现还是这一种漏洞可能对于渗透打点没啥意义,但在内网获取主机之后进行权限持久化维持是有很大的用处的。 [TOC] ## DLL概念 动态链接库(Dynamic-Link-Library,缩写dll), 是微软公司在微软windows操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名是.DLL、.OCX(包含ActiveX控制的库)或者.DRV(旧式的系统的驱动程序) 每一个DLL都会有一个入口函数,他是DLLMain,和在c开发当中的main函数是一样的都作为程序/dll的入口段,系统在特定环境下会调用DLLMain,一般为: - 进程装载DLL - 进程卸载DLL - DLL在被装载之后创建了新线程 - DLL在被装载之后一个线程被终止 应用程序导入函数与DLL文件中的导出函数进行链接有两种方式:隐式链接(load-time dynamic linking)也叫静态调用和显式链接(run-time dynamic linking)也叫动态调用。隐式链接方式一般用于开发和调试,而显示链接方式就是我们常见的使用LoadLibary或者LoadLibraryEx函数来加载DLL去调用相应的导出函数。调用LoadLibrary或者LoadLibraryEx函数可以使用DLL的相对路径也可以使用绝对路径,如果使用相对路径,那么windows就会按照一定的顺序去搜索,那么此时问题就来,这个地方就会产生漏洞。 ## DLL加载顺序 DLL是以文件的形式存在在硬盘中,Microsoft官方完整记录了DLL搜索顺序。 微软的DLL劫持可以分为三个阶段: - 无保护阶段:Windows XP SP2之前 - 保护阶段:Windows XP SP2之后,Windows 7之前 - 进一步保护阶段:Windows 7之后 ### Windows XP SP2之前,DLL文件的加载顺序如下: - 进程对应的应用程序所在目录; - 加载 DLL 时所在的当前目录; - 系统目录即 SYSTEM32 目录(通过 GetSystemDirectory 获取); - 16位系统目录即 SYSTEM 目录; - Windows目录(通过 GetWindowsDirectory 获取); - PATH环境变量中的各个目录; ### Windows XP SP2之后: 微软为了防止DLL劫持漏洞的产生,在XP SP2之后,添加了一个**SafeDllSearchMode**的注册表属性。注册表路径如下: ``` HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode ``` 当SafeDllSearchMode的值设置为1,即安全DLL搜索模式开启时(“安全DLL查找模式”默认是启用的),查找DLL的目录顺序如下: - 应用程序所在目录; - 系统目录SYSTEM32目录; - 16位系统目录即SYSTEM 目录。该项只是为了向前兼容的处理,可以不考虑; - Windows目录。通常是C:\Windows; - 加载 DLL 时所在的当前目录; - 环境变量PATH中所有目录。需要注意的是,这里不包括App Paths注册表项指定的应用程序路径。 ### Windows 7之后: 微软为了更进一步的防御系统的DLL被劫持,将一些容易被劫持的系统DLL写进了一个注册表项中,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用。注册表路径如下: ``` HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs ``` ![](/download/b9eac02f-7242-40d7-af49-37beed7b654d.png) ## 如何寻找DLL劫持漏洞 1. 启动应用程序 2. 使用Process Monitor等类似软件查看应用程序启动后加载的动态链接库 3. 从该应用程序已经加载的DLL列表中,查找在上述"KnownDLLs注册表"中不存在的DLL(一般寻找没有的dll而不是删除已有的dll) 4. 编写从上一步获取到DLL的劫持DLL 5. 将编写好的劫持DLL放到应用目录下,重新启动该应用程序,检测是否劫持成功。 如果以上没有利用成功可能存在一下问题: ● DLL不再KnownDLL是注册表中但是已经被微软保护,比如ntdll.dll ● 宿主进程在调用LoadLibrary函数使用了绝对路径 ● 宿主进程在对调用DLL进行了文件合法性校验 ● 宿主调用DLL时候使用了SetDllDirectory函数把当前目录从DLL的搜索顺序列表中删除 ## 如何利用DLL劫持漏洞 编写一个用于劫持DLL文件,需要两个步骤: 1.查看被劫持的DLL的导出函数表。 2.编程实现劫持DLL向原DLL的导出函数的转发,并加入你的“恶意代码”。 ## DLL劫持防护 A. 对于外部第三方DLL和自己的DLL: 1. 使用LoadLibrary API加载DLL时使用绝对路径,类似的情况还包括其他API如LoadLibraryEx, CreateProcess, ShellExecute等; 2. 将所有需要使用到的DLL放在应用程序所在的目录,不放到系统目录或者其他目录。 B. 对于系统共享的DLL(如user32.dll, mfc80loc.dll等),不能放到程序目录下时,应该: 1. 使用绝对路径加载; 2. 对于无法准确预测绝对路径的情况,可以使用相对路径加载。 C. 程序启动时调用API SetDllDirectory(L"")将当前目录从DLL加载顺序中移除. 其次,对于广义DLL劫持漏洞,我们只有一种有效的处理办法:加载任何DLL前先校验文件的签名,签名没有问题的才能加载。 ## 实战挖掘利用 选择软件:autoclickers.exe ![](/download/ef640021-b5d9-4efd-889b-210861c50831.png) ![](/download/8def9431-31f0-4542-8d50-6724285f0ec5.png) ![](/download/d5cc6eab-4147-4285-9865-ea2ae3bc4909.png) 貌似不行 ![](/download/6a5f1061-3381-4ac2-97ee-ffa0524aaa8b.png) ![](/download/5d8b5793-bffb-4ebf-a6c7-94e5a512b338.png) 换一个ShadowSocks和补丁 ![](/download/39f8e255-f670-4cfb-a7ee-e369797f07cb.png) 啥 ![](/download/b4f41121-75b3-48a2-874e-5b300660b7b6.png) 再换一个mattermost ![](/download/9c4fdf3f-d872-4997-97a9-792095e545e1.png) ![](/download/9028af79-8861-4ec9-b7c5-1f963ad454b7.png) 冲 ![](/download/6bcbd83e-4cf0-4003-a041-712afb14ccd0.png) 成了 ## 实战利用:dll proxy保留原dll功能 https://github.com/tothi/dll-hijack-by-proxying 这边以keepass进行举例 执行以下操作 ``` root@kali:~/Tools/Red-Team/windows/dll-hijack-by-proxying# ./gen_def.py /mnt/hgfs/Tools/version_orig.dll > test.def root@kali:~/Tools/Red-Team/windows/dll-hijack-by-proxying# i686-w64-mingw32-gcc -shared -o /mnt/hgfs/Tools/test.dll version.c test.def root@kali:~/Tools/Red-Team/windows/dll-hijack-by-proxying# ``` 对刚才test重命名 ![](/download/042e29cc-c7d6-45de-bda7-2453d5fd0e38.png) 全部移入到keepass目录下 ![](/download/46d3a761-4f7d-4cc6-8b51-1fe40aeb8eb9.png) 打开没有反应啥玩意 后面用PElab看了下发现上面gen def的时候接的是/mnt/hgfs/Tools/version_orig.dll,所以forward的出了问题 ![](/download/bd31bf7b-ee57-49ef-8439-b95a48d783c2.png) ``` root@kali:~/Tools/Red-Team/windows/dll-hijack-by-proxying# cp /mnt/hgfs/Tools/version_orig.dll version_orig.dll root@kali:~/Tools/Red-Team/windows/dll-hijack-by-proxying# ./gen_def.py version_orig.dll > test.def root@kali:~/Tools/Red-Team/windows/dll-hijack-by-proxying# i686-w64-mingw32-gcc -shared -o /mnt/hgfs/Tools/version.dll version.c test.def ``` PELab看一下貌似没问题 ![](/download/d7375164-00c2-4c5b-bc9f-5e678593de98.png) 重新运行啥都没有 这边又有一个坑,我们选择的是x64的dll,编译的时候编译成了x86 > i686-w64-mingw32-gcc -shared -o /mnt/hgfs/Tools/version.dll version.c test.def 应该是 > x86_64-w64-mingw32-gcc -shared -o /mnt/hgfs/Tools/IPHLPAPI.DLL version.c test.def -s ![](/download/7f3135ed-a2e2-4eeb-bd25-09e88ac1891e.png) ## 工具使用 https://github.com/sensepost/rattler https://github.com/MojtabaTajik/Robber https://github.com/cyberark/DLLSpy https://github.com/coca1ne/DLL_Hijacker https://github.com/tothi/dll-hijack-by-proxying ## 参考资料 https://earthmanet.github.io/posts/2021/02/dll%E5%8A%AB%E6%8C%81%E5%8F%8A%E5%85%B6%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98/ https://blog.csdn.net/weixin_44948325/article/details/106962772 https://blog.csdn.net/whl0071/article/details/108861509 https://security.tencent.com/index.php/blog/msg/20 https://www.ascotbe.com/2020/11/13/DynamicLinkLibraryHijack/ https://3gstudent.github.io/DLL%E5%8A%AB%E6%8C%81%E6%BC%8F%E6%B4%9E%E8%87%AA%E5%8A%A8%E5%8C%96%E8%AF%86%E5%88%AB%E5%B7%A5%E5%85%B7Rattler%E6%B5%8B%E8%AF%95