挖掘DLL劫持漏洞-白加黑的实现
2022-02-21 16:46:23

0x00 前言

首发在sec-in

前段时间比较忙,好久没发东西了。最近重新学习了DLL劫持漏洞相关,想着分享几个案例。

0x01 DLL劫持漏洞

首先从挖洞角度来看,这个洞对厂商没有什么太大的意义,可能都会被忽略掉。但是从Windows系统持久化维权角度来看,是非常常用也很好用的方法之一,部分公司的某些产品在很多计算机都有默认安装,如果存在DLL劫持漏洞,那么作为白+黑(白程序+黑DLL)权限维持就很容易了。

1.原理:

首先来理解DLL在Windows系统中的作用,DLL全称Dynamic Link Library,称为动态连接库,在Windows系统中,大多数程序并不是一个单独的可执行文件,而是有一些单独的存放动态链接库在系统中,当需要某些功能时,通过DLL执行相应的功能,即DLL调用。

那么既然程序执行某些功能时,可能会通过DLL调用,从利用角度来看,如果替换了这个DLL文件,或者导出了原DLL的导出函数并恶意构造,在原程序调用正常DLL前调用了我们预先构造好的恶意DLL,那么就达到了劫持的效果。

所以手法可以有多种,比如替换原DLL文件,黑DLL放置最先路径,DLL转发等。

2.DLL搜索顺序

这里还需要提及另一个概念。正常程序运行时,应用程序可以通过指定完整路径、使用 DLL重定向或通过使用清单 来控制 DLL 的加载 位置

如果未使用这些方法,则系统在加载时搜索 DLL。参考文档如下:

https://docs.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order#factors-that-affect-searching

这里有几个比较重要的概念单独摘了出来。

在系统搜索 DLL 之前,它会检查以下内容:

  • 如果内存中已加载同一模块名称的 DLL,则无论加载的 DLL 位于哪个目录中,系统都使用加载的 DLL。 系统不搜索 DLL。
  • 如果 DLL 位于运行应用程序的 Windows 版本的已知 DLL 列表中,则系统将使用其已知 DLL (的副本和已知 DLL 的依赖 DLL(如果有) )。 系统不搜索 DLL。 有关当前系统上的已知 DLL 的列表,请参阅以下注册表项 :HKEY _ LOCAL MACHINE _ SYSTEM CurrentControlSet Control Session Manager KnownDLLs。

如果 DLL 具有依赖项,则系统会搜索依赖 DLL,就像它们仅用模块名称加载一样。 即使通过指定完整路径加载了第一个 DLL,也是如此。

再者就是标准DLL搜索顺序,取决于一个叫做保险箱DLL 搜索模式(SafeDllSearchMode)的Windows机制。

Windows XP sp2前:

Windows查找DLL的目录以及对应的顺序:

  1. 进程对应的应用程序所在目录;
  2. 当前目录(Current Directory);
  3. 系统目录(通过 GetSystemDirectory 获取);
  4. 16位系统目录;
  5. Windows目录(通过 GetWindowsDirectory 获取);
  6. PATH环境变量中的各个目录;

winxdows XPs sp2后:

默认启动了SafeDllSearchMode,注册表查询,开启则键值为1:

1
reg query "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode"
  1. 进程对应的应用程序所在目录(可理解为程序安装目录比如C:ProgramFilesuTorrent);
  2. 系统目录(即%windir%system32);
  3. 16位系统目录(即%windir%system);
  4. Windows目录(即%windir%);
  5. 当前目录(运行的某个文件所在目录,比如C:DocumentsandSettingsAdministratorDesktoptest);
  6. PATH环境变量中的各个目录;请注意,这不包括应用路径注册表项指定的 每个应用程序路径 。 计算 DLL 搜索路径时,不会使用应用路径密钥。

如果 SafeDllSearchMode 已禁用,则搜索顺序如下所示:

  1. 从其中加载应用程序的目录。
  2. 当前目录。
  3. 系统目录(即%windir%system32);
  4. 16 位系统目录(即%windir%system);
  5. Windows目录(即%windir%);
  6. PATH 环境变量中的各个目录;请注意,这不包括应用路径注册表项指定的 每个应用程序路径 。 计算 DLL 搜索路径时,不会使用应用路径密钥。

Windows 7后:

系统默认不在存在SafeDllSearchMode,使用了KnownDLLs列表,注册表查询如下:

1
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs"

例Windows 10 KnowDLLs:

image-20220218143209777

所以,看完这些机制,如果想要尝试DLL劫持,只需要放置黑DLL在优先于正常DLL的目录,并且DLL本身不在Windows KnowDLLs列表中,就能达到DLL劫持的效果。

0x02 准备一个测试DLL

1.环境目标

开发环境:Visual Studio 2019

DLL功能:弹计算器

2.代码:

dllmain.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# include "pch.h"
# include <stdlib.h>

BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: //DLL进程装载
system("calc");
case DLL_THREAD_ATTACH: //DLL线程装载
case DLL_THREAD_DETACH: //DLL线程卸载
case DLL_PROCESS_DETACH: //DLL进程卸载
break;
}
return TRUE;
}

代码很简单实现,弹计算器即可,这里需要编译一个x64和一个x86的dll,运行库指定MT,编译即可。

image-20220218162332082

0x03 挖掘劫持的场景

下面分享下几种场景下的DLL劫持。

1.劫持存在的DLL

场景如下:已知目标存在必然会被使用的某个程序,希望劫持当前程序调用的DLL,所以在本地模拟了当前程序运行过程,发现了一个被调用的DLL。

这里用到了一个很好用的工具:procmon,这款工具是微软官方出的进程监视器,用于显示实时文件系统、注册表和进程/线程活动,这里用于观察进程运行过程的DLL调用。

https://docs.microsoft.com/zh-cn/sysinternals/downloads/procmon

这里拿navicat.exe来举例子。

通过运行procmon设置filter,运行navicat.exe观察DLL调用。

procmon的filter规则,可以设置指定processname,并且观察原程序path路径下的文件调用。如图所示配置filter:

image-20220218165920806

image-20220218165955324

之后配置完filter运行navicat.exe,观察到当前libcurl.dll的stack,我们看到了LoadLibrary函数,说明当前DLL是被动态调用的,一般DLL中会存在多个导出函数,通过另一个工具api monitor我们可以观察到navicat.exe原程序,具体用了哪个导出函数,方便我们写一个转发DLL,或者也可以直接用工具转发。

古老工具:

DLL_Hijacker:https://github.com/zhaoed/DLL_Hijacker

aheadlib:

image-20220218170119438

通过判断使用了哪个导出函数可以定制修改指定的导出函数(在指定位置加入恶意代码)。

对于这类已经存在的DLL,也可以直接用工具生成转发DLL,这里使用了aheadlib生成转发DLL的cpp代码。

image-20220221010425536

既然是劫持存在的DLL,通过DLL转发需要修改原DLL名称,如果转发了所有的导出函数,这里加入恶意代码可以直接在DllMain入口函数加入恶意代码。

image-20220221010606656

通过aheadlib生成的DLL指定的原DLL名加上了ORG,这里修改原DLL名为libcurlOrg.dll。修改生成的DLL名为libcurl.dll。

image-20220221010723910

需要注意DLL的位数。这里原来的libcurl.dll是x86位,生成x86 DLL,字符集多字节字符集,生成运行库无依赖多线程(MT)。

运行navicat成功执行恶意代码。

image-20220221012234844

2.劫持不存在的DLL

场景如下:程序在运行时,会尝试加载某些可能在当前环境不存在的DLL,通过放入我们的恶意DLL,在预先加载DLL的路径执行我们的DLL(多用于维权)。

还是拿navicat来举例。通过procmon观察。

这里设置Filter需要稍做修改。

  • Process name is navicat.exe
  • Result is NAME NOT FOUND
  • Path ends with .dll

image-20220221015533777

运行navicat.exe,复制提取procmon拦截的内容,最终提取下DLL,这里可以用一种批量的验证工具,感觉挺好用的,推荐ChkDllHijack。

image-20220221015914567

选择劫持的程序,贴入需要验证的DLL即可,测试DLL倒也可以手工替换为自己的,或者干脆就自己手工测试。

image-20220221020233105

手工测试的话,查看procmon,如果当前DLL不存在,并且在stack中看到调用了LoadLibrary函数,那么放入目录我们的DLL文件即可劫持。

image-20220221020510792

最终效果如下。

image-20220221020704015

0x04 白加黑思路场景汇总

这里其实主要是思路,关于劫持DLL这块其实Defenfer杀的比较死,一般来说DLL都可以直接被拿来分析,但是一些特殊场景还是可以用的。

举个例子,某内网机器出网,但是只能smb连接上传下载文件,没有创建服务执行命令权限,存在某常用软件,这个时候就可以放一个黑DLL进去,当程序被运行时,以此进行维权操作or上线。

再者就是,通过打包上传白文件(exe)+黑(DLL),执行白文件,用以目标主机维权or上线等等。

思路场景有很多,最近项目上也遇到了。这里还是可以挖掘一下常用的软件,还是有蛮多DLL劫持的。