Hijack DLL新姿势

  • A+

译文声明
本文是翻译文章,文章原作者Justin Bui ,文章来源:https://posts.specterops.io
原文地址:https://posts.specterops.io/automating-dll-hijack-discovery-81c4295904b0

前言

这篇文章将给大家阐述动态链接库(DLL)搜索顺序劫持的概念,以及如何在Windows下保持持久性。该技术在MITER ATT&CK框架中收录,详细信息请查看DLL搜索顺序劫持(T1038)

DLL劫持一直深受黑客们的喜欢,利用此技术可以实现启动木马后门,游戏外挂插件的注入,绕过UAC等操作。本文将介绍动态链接库基本概念,动态链接库的搜索顺序和DLL劫持,之后我们将探讨自动化发现DLL劫持的方法(https://github.com/slyd0g/DLLHijackTest),着重分析通过劫持自启动程序的DLL实现持久性控制的全过程。例如,Slack和Microsoft Teams在默认情况下是自启动的,我们可以通过劫持应用程序中DLL实现持久性控制的目的。

最后感谢的的同时Josiah Massari(@ Airzero24)对我带来的帮助。

什么是DLL?

DLL是一个包含可由多个程序,同时使用的代码和数据的库。

Windows应用程序通常会使用LoadLibrary*函数将指定的模块加载到调用进程的地址空间中。应用程序可以自己编写dll并进行调用,也可以使用磁盘上System32中存在的dll。

例如,开发人员需要发送HTTP请求就可以使用WinHTTP库(winhttp.dll)来代替使用原始套接字实现HTTP请求。

DLL搜索顺序和劫持

DLL是以文件的形式存在在硬盘中,那么应用程序又是如何索引所需的DLL呢?其实,Microsoft已在此处完整记录了DLL搜索顺序。

从Windows XP SP2开始,"安全DLL查找模式"默认是启用的, 启用"安全DLL查找模式"时,查找顺序如下:
1. 应用程序所在目录;
2. 系统目录。GetSystemDirectory返回的目录,通常是系统盘WindowsSystem32;
3. 16位系统目录。该项只是为了向前兼容的处理,可以不考虑;
4. Windows目录。GetWindowsDirectory返回的目录,通常是系统盘Windows;
5. 当前目录。
6. 环境变量PATH中所有目录。需要注意的是,这里不包括App Paths注册表项指定的应用程序路径。

一个操作系统可以包含同一动态链接库的多个版本。应用程序可以通过指定完整路径,或使用manifest的方式来加载DLL。

如果应用程序没有指定DLL加载位置,则Windows将默认使用上述搜索次序。DLL搜索顺序中的第一个位置,即加载应用程序的目录,是攻击者感兴趣的。

DLL劫持是指通过一些手段来劫持或者替换正常的DLL,欺骗正常程序加载预先准备好的恶意DLL。例如开发人员想要调用C:Windowssystem32中的dll,但由于windows优先搜索当前路径,所以当我们把恶意dll放在应用程序同一路径下,便会被程序成功加载,从而执行恶意操作。

使用DLL劫持进行持久化

当受害靶机打开遭受DLL劫持的应用程序或服务时,就可以保持持久性。我的同事@ Airzero24在Microsoft OneDrive,Microsoft Teams和Slack中发现了DLL劫持,被劫持的动态链接库文件为userenv.dll。

我们一般是有针对性的选择劫持的程序,尽量选择自启动应用程序,如下图所示:
::: hljs-center

1.png
Windows应用程序配置为自启动

:::

为了验证DLL劫持的发生,我手工构建了一个DLL shellcode加载器,它的作用是启动Cobalt Strike Beacon。我将恶意的DLL重命名为userenv.dll,并将其复制到易受攻击的应用程序的目录中。运行该程序,确实看到了一个新的Beacon回调。
::: hljs-center

2.png
通过DLL劫持进行Cobalt Strike Beacon

:::

我们还可以使用Process Explorer,来验证应用程序确实加载了恶意DLL。
::: hljs-center

3.png
Process Explorer显示恶意DLL已加载

:::

自动化DLL劫持实现

在进行了之前的测试后,我们试图找到其它可用于DLL劫持的目标。

我所使用的测试代码可以在这里找到。

案例分析:Slack

这是我使用的过滤条件:
•进程名称为slack.exe
•结果包含 NOT FOUND
•路径以.dll结尾

::: hljs-center

4.png
使用ProcMon进行筛选

:::

设置好搜索条件后,启动Slack。运行结果如下图:
::: hljs-center

5.png
ProcMon发现潜在的DLL劫持

:::

我将这些数据导出为CSV文件,以便在PowerShell中进行解析。

为了实现DLL劫持的自动化,确定Slack成功加载的DLL名称。我编写了一个新的DLL,调用GetModuleHandleEx和GetModuleFileName确定加载DLL的名称,并将结果写入到指定的文本文件。

我接下来要做的工作就是解析CSV文件,获取DLL路径列表。然后遍历路径列表,将测试的DLL复制到指定路径,启动目标进程,随后停止目标进程并删除测试的DLL文件。如果DLL成功加载,我们可以在指定文件看到DLL文件名。

当测试完成后,我期望在文本文件中看到可用于DLL劫持的路径列表

我采用PowShell脚本实现了上述操作,它接受ProcMon所生成的CSV文件的路径,恶意DLL的路径,要启动进程路径以及想要传递给进程的任何参数。

::: hljs-center

6.png
Get-PotentialDLLHijack所需参数

:::

::: hljs-center

7.png
Get-PotentialDLLHijack.ps1

:::

脚本执行完成后,我们查看之前指定输出的文本文件,在文本文件中记录了可以进行dll劫持的几个目标:
PS C:Users John Desktop> Get-PotentialDLLHijack -CSVPath。 Logfile.CSV -MaliciousDLLPath。 DLLHijackTest.dll -ProcessPath“ C: Users John AppData Local slack slack.exe”C: Users John AppData Local slack app-4.6.0 WINSTA.dll
C: Users John AppData Local slack app-4.6.0 LINKINFO.dll
C: Users John AppData Local slack app-4.6.0 ntshrui.dll
C: Users John AppData Local slack app-4.6.0 srvcli.dll
C: Users John AppData Local slack app-4.6.0 cscapi.dll
C: Users John AppData Local slack app-4.6.0 KBDUS.DLL

案例分析:Microsoft Teams

让我们再次执行上述过程:
1.使用ProcMon工具找出可能存在的DLL劫持,将搜索结果导出为CSV文件。
2.确定启动进程的路径。
3.确定要传递给进程的所有参数。
4.使用适当的参数运行Get-PotentialDLLHijack.ps1。

在Microsoft Teams中发现了以下可以于劫持的dll:
PS C:Users John Desktop> Get-PotentialDLLHijack -CSVPath。 Logfile.CSV -MaliciousDLLPath。 DLLHijackTest.dll -ProcessPath“ C: Users John AppData Local Microsoft Teams Update.exe” -ProcessArguments '--processStart“ Teams.exe”'C: Users John AppData Local Microsoft Teams current WINSTA.dll
C: Users John AppData Local Microsoft Teams current LINKINFO.dll
C: Users John AppData Local Microsoft Teams current ntshrui.dll
C: Users John AppData Local Microsoft Teams current srvcli.dll
C: Users John AppData Local Microsoft Teams current cscapi.dll
C: Users John AppData Local Microsoft Teams current WindowsCodecs.dll
C: Users John AppData Local Microsoft Teams current TextInputFramework.dll

案例分析:Visual Studio Code

如法炮制,我在Visual Studio Code中找出来以下可用于劫持的dll:
PS C:Users John Desktop> Get-PotentialDLLHijack -CSVPath。 Logfile.CSV -MaliciousDLLPath。 DLLHijackTest.dll -ProcessPath“ C: Users John AppData Local Programs Microsoft VS Code Code.exe”C: Users John AppData Local Programs Microsoft VS Code WINSTA.dll
C: Users John AppData Local Programs Microsoft VS Code LINKINFO.dll
C: Users John AppData Local Programs Microsoft VS Code ntshrui.dll
C: Users John AppData Local Programs Microsoft VS Code srvcli.dll
C: Users John AppData Local Programs Microsoft VS Code cscapi.dll

共享的DLL劫持

我注意到Slack,Microsoft Teams和Visual Studio Code都调用了以下的dll:
•WINSTA.dll
•LINKINFO.dll
•ntshrui.dll
•srvcli.dll
•cscapi.dll
这确实是一个有趣的现象,您只需要劫持其中一个DLL文件就是同时控制3个应用程序执行流程。下面让我们深入分析一下这几个dll文件。

方法

我通过观察Slack加载WINSTA.dll,LINKINFO.dll,ntshrui.dll,srvcli.dll,和cscapi.dll时堆栈轨迹来寻找答案。

延迟加载的DLL

我发现程序在加载WINSTA.dll,LINKINFO.dll,ntshrui.dll,和srvcli.dll有诸多相似之处。

8.png
Code.exe尝试加载WINSTA.dll时的堆栈轨迹

::: hljs-center

9.png

Teams.exe尝试加载LINKINFO.dll时的堆栈轨迹

:::

::: hljs-center

10.png
Slack尝试加载ntshrui.dll时的堆栈轨迹

:::

我认为导致这种行为的原因应该与延迟加载DLL有关。通过对WINSTA.dll的堆栈跟踪,我们发现负责延迟加载的模块是wtsapi32.dll。

我在Ghidra中打开wtsapi32.dll,使用Search->For Strings->Filter: WINSTA.dll,然后双击即可找到WINSTA.dll在内存的位置。

::: hljs-center

11.png
wtsapi32.dll中的“ WINSTA.dll”字符串

:::

我们可以右击,选中References查看该地址的引用情况。
::: hljs-center

12.png
以下对WINSTA.dll的引用

:::

通过查看引用情况,我们可以看到WINSTA.dll字符串被传入ImgDelayDescr结构体中。通过查看此结构的相关文档,我们基本可以确定它的确与延迟加载DLL有关。
typedef struct ImgDelayDescr {
DWORD grAttrs; // attributes
RVA rvaDLLName; // RVA to dll name
RVA rvaHmod; // RVA of module handle
RVA rvaIAT; // RVA of the IAT
RVA rvaINT; // RVA of the INT
RVA rvaBoundIAT; // RVA of the optional bound IAT
RVA rvaUnloadIAT; // RVA of optional copy of original IAT
DWORD dwTimeStamp; // 0 if not bound,
// O.W. date/time stamp of DLL bound to (Old BIND)
} ImgDelayDescr, * PImgDelayDescr;

此结构会传递给_delayLoadHelper2,它将调用LoadLibrary/ GetProcAddress加载指定的DLL,并在IAT表中patch导入函数的地址。
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd, //Const pointer to a ImgDelayDescr struct
FARPROC * ppfnIATEntry //A pointer to the slot in delay load IAT
);

在__delayLoadHelper2函数中又调用了ResolveDelayLoadedAPI。
::: hljs-center

13.png
Ghidra中的__delayLoadHelper2和ResolveDelayLoadedAPI

:::

这与我们之前在Slack加载时观察到的堆栈变化相吻合。
::: hljs-center

14.png
ProcMon中的__delayLoadHelper2和ResolveDelayLoadedAPI

:::

每个延迟加载的DLL之间的主要区别是“父” DLL。在所有三个应用程序中:
•wtsapi32.dll 延迟加载 WINSTA.dll
•shell32.dll 延迟加载 LINKINFO.dll
•LINKINFO.dll 延迟加载 ntshrui.dll
•ntshrui.dll 延迟加载 srvcli.dll

NetShareGetInfo和NetShareEnum中的DLL劫持

当Slack尝试加载cscapi.dll时,我看到一个源自于srvcli.dll的LoadLibraryExW调用。

::: hljs-center

15.png
加载cscapi.dll时的堆轨迹

:::

我们同样把srvcli.dll在(Ghidra)中打开,依次选择Search -> For Strings -> Filter: cscapi.dll,双击找到字符串,来到LoadLibrary调用处。
::: hljs-center

16.png

srvcli.dll在cscapi.dll上调用LoadLibrary

:::

继续顺藤摸瓜,我们找到了两个调用该模块的函数:
•NetShareEnum
•NetShareGetInfo

::: hljs-center

17.png
NetShareEnum加载cscapi.dll

:::

18.png

NetShareGetInfo加载cscapi.dll
我还通过POC验证了这一点:

::: hljs-center

19.png
NetShareEnum.exe加载cscapi.dll

:::

::: hljs-center

20.png
NetShareGetInfo.exe加载cscapi.dll

:::

结果

Slack存在以下可用于hijack的dll:
C: Users John AppData Local slack app-4.6.0 WINSTA.dll
C: Users John AppData Local slack app-4.6.0 LINKINFO.dll
C: Users John AppData Local slack app-4.6.0 ntshrui.dll
C: Users John AppData Local slack app-4.6.0 srvcli.dll
C: Users John AppData Local slack app-4.6.0 cscapi.dll
C: Users John AppData Local slack app-4.6.0 KBDUS.DLL

Microsoft Teams中存在以下可用于hijack的dll:
C: Users John AppData Local Microsoft Teams current WINSTA.dll
C: Users John AppData Local Microsoft Teams current LINKINFO.dll
C: Users John AppData Local Microsoft Teams current ntshrui.dll
C: Users John AppData Local Microsoft Teams current srvcli.dll
C: Users John AppData Local Microsoft Teams current cscapi.dll
C: Users John AppData Local Microsoft Teams current WindowsCodecs.dll
C: Users John AppData Local Microsoft Teams current TextInputFramework.dll

Visual Studio Code中存在以下可用于hijack的dll:
C: Users John AppData Local Programs Microsoft VS Code WINSTA.dll
C: Users John AppData Local Programs Microsoft VS Code LINKINFO.dll
C: Users John AppData Local Programs Microsoft VS Code ntshrui.dll
C: Users John AppData Local Programs Microsoft VS Code srvcli.dll
C: Users John AppData Local Programs Microsoft VS Code cscapi.dll

另外,我还意外发现在NetShareEnum和NetShareGetInfo中使用硬编码调用cscapi.dll,这同样可以导致一个DLL劫持。

结论

简而言之,DLL劫持是攻击者在已签名/受信任的应用程序中获得代码执行的一种方法。我通过编写自动化脚本来快速查找可用于劫持的dll,并在 Slack,Microsoft Teams和Visual Studio Code进行了功能验证,证明了它的可行性。

在实验中,我还观察到了在三个应用程序中共享的几个可用于劫持的DLL,并深入探究了其中的机制。在此过程中还意外发现了两个有趣的API函数调用,它们将DLL劫持引入到了任何调用它们的程序。
•NetShareEnum加载cscapi.dll
•NetShareGetInfo加载cscapi.dll

相关推荐: 记一次渗透实战

目标:http://xxx.xx.xxx.xx:9002/zxxxxxg/bxm/login.php 访问目标: 完事不慌burp抓包 ,查看响应包 尝试枚举跑中国人名字典爆破无果 常规操作 ,对目标目录进行删减 ,发现了目录遍历的漏洞 http://xxx.…