本文将介绍动态链接库(DLL)搜索顺序劫持的概念,以及如何实现在Windows上的持久化攻击。MITRE ATT&CK对DLL搜索顺序劫持的描述详见DLL Search Order Hijacking (T1038)。
DLL劫持对攻击者很有用,原因有很多,但本文会重点介绍与开机自启程序结合使用时的持久化攻击。例如,由于Slack和Microsoft Teams开机自启(默认情况下),因此,只要用户登录,这些应用程序中的DLL劫持将允许攻击者能够持久访问攻击目标。
在介绍DLL、DLL搜索顺序和DLL劫持的概念后,我会阐述DLL劫持自动化检测的过程https://github.com/slyd0g/DLLHijackTest。本文将涉及Slack、Microsoft Teams以及Visual Studio Code相关的DLL劫持检测。
最后,我注意到很多DLL劫持会同时作用于多个程序。我调查了底层原因,发现调用某些位于C:\Windows\System32\下的Windows API的程序容易成为DLL劫持的目标。
这里我要感谢我的同事,Josiah Massari (@Airzero24)。是他最早发现的这些DLL劫持案例并解释其方法和原理,促成了我将检测做成自动化。
DLL是一个包含可被多个程序同时使用的代码和数据的库。(定义源于Microsoft)
Windows程序可以调用LoadLibrary*系列函数来使用DLL中的功能。程序可以引用自定义的DLL,也可以引用System32中Windows自带的DLL。开发者可以加载System32中的DLL,使其程序使用Windows的现成功能,而不必重新造轮子。
例如,开发者需要发起HTTP请求时,可以使用WinHTTP库(winhttp.dll),而无需用原始套接字开发并实现HTTP功能。
由于DLL也是存储在磁盘上的文件,那么程序是如何知道从哪里加载DLL的呢?Microsoft在此详细记录了DLL搜索顺序。
自Windows XP SP2起,安全DLL搜索模式默认开启(HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode)。当安全模式启用时,搜索顺序如下:
系统可包含动态链接库(DLL)的多个版本。程序可通过指定完整路径或使用比如manifest的其他机制来控制加载特定的DLL。(定义源于Microsoft)
如果程序没有加载指定的DLL,Windows会默认执行上述的DLL搜索流程。DLL搜索的第一顺位,即程序的安装目录,是攻击者最感兴趣的。
如果开发者想要从C:\Windows\System32\加载 DLL,但并没有写清楚让程序这么做,那么程序安装目录下的恶意DLL就会优先于System32下的合法DLL被加载。这里恶意DLL指的是DLL劫持,攻击者用来在受信/签名程序中加载恶意代码。
当目标程序/服务启动并且恶意DLL被植入到了关键位置,DLL劫持即可用于持久化攻击。我的同事@Airzero24,发现了Microsoft OneDrive、 Microsoft Teams、以及Slack中针对userenv.dll的劫持。
这类程序被盯上的原因是,他们默认都会随着Windows开机启动。任务管理器中可以看到:
为了验证DLL劫持,我写了一段运行Cobalt Strike Beacon的DLL shellcode,命名为userenv.dll,并且复制到目标程序的安装目录。运行程序,我看到肉鸡上线了。
查看Process Explorer,可以确认恶意DLL确实被目标程序加载了。
确认先前已知的DLL劫持之后,我想看看是否可以找到其他的DLL劫持。
测试代码在此:https://github.com/slyd0g/DLLHijackTest
首先,运行Process Monitor(ProcMon),过滤规则如下:
接下来,运行Slack,留意Slack搜索但无法找到的DLL。
从ProcMon中导出数据,方便用PowerShell处理。
用当前加载shellcode的DLL,我无法轻易找出被Slack加载的DLL的名字。我写了一个新的使用了GetModuleHandleEx和GetModuleFileNameDLL的DLL,输出加载DLL的名字到文本文件。
接下来就是解析CSV得到DLL路径列表,遍历路径列表,把我的测试DLL复制到特定路径,运行目标进程,退出目标进程,删除测试DLL。如果测试DLL被成功加载,其文件名会写入结果文件。
上述步骤完成后,会得到一个有效DLL劫持的列表。
在我的DLLHijackTest项目中的PowerShell脚本可以处理所有步骤。脚本的输入参数有:ProcMon生成的CSV文件的路径,恶意DLL的路径,目标程序的路径,以及其他你想要传递给目标程序的参数。
几分钟后,打开“恶意”DLL中指定的文本文件,发现以下针对Slack的DLL劫持:
重复上述过程:
发现以下针对Microsoft Teams的DLL劫持:
注意:我这里修改了下PowerShell脚本,终止的进程是Teams.exe,启动的进程是Update.exe。
重复以上关键步骤,发现针对Visual Studio Code的劫持,如下:
我发现Slack、Microsoft Teams和Visual Studio Code都会出现以下DLL劫持:
我觉得很有趣,尝试挖掘造成此现象的底层原因。
我观察了下Slack加载WINSTA.dll、LINKINFO.dll、ntshrui.dll、srvcli.dll和cscapi.dll的栈轨迹。
我注意到当WINSTA.dll、LINKINFO.dll、ntshrui.dll、srvcli.dll和cscapi.dll被加载时栈轨迹存在共性。
栈轨迹都调用了_tailMerge_<dllname>_dll和delayLoadHelper2,之后还调用了LdrResolveDelayLoadedAPI。三个程序都出现了这种情况。
我认为这种情况与延迟加载DLL有关。当WINSTA.dll被加载时,从栈轨迹可以看到,wtsapi32.dll导致了延迟加载。
在Ghidra中打开wtsapi32.dll,设置Search -> For Strings -> Filter: WINSTA.dll。双击查找的结果会看到其内存地址。
右键点击内存地址,可以找到对该地址的引用。
我们看到WINSTA.dll被传递至名为ImgDelayDescr的结构体。查看该结构体的说明文档,可以确定其与延迟加载DLL有关。
该结构体可被传递至__delayLoadHelper2,后者会通过LoadLibrary/GetProcAddress加载特定DLL,并且会修正延迟加载导入地址表(IAT)中导入函数的地址。
查找其他对ImgDelayDescr结构体的引用,发现__delayLoadHelper2调用,后者之后调用了ResolveDelayLoadedAPI。为方便理解,我重命名了函数名、类型和变量。
很好!这与我们之前在Slack加载WINSTA.dll时在ProcMon中看到的栈轨迹一致。
WINSTA.dll、LINKINFO.dll、ntshrui.dll和srvcli.dll的行为是一致的,每个延迟加载的DLL间主要的不同点是“父级”DLL。在三个程序中:
看到有意思的点了吗?shell32.dll加载了LINKINFO.dll,LINKINFO.dll加载了ntshrui.dll,ntshrui.dll最后加载了srvcli.dll。 最后我们看到共有的针对cscapi.dll的劫持。
观察Slack加载cscapi.dll的栈轨迹,看到调用了LoadLibraryExW,后者似乎来源于srvcli.dll。
在Ghidra中打开srvcli.dll,设置Search -> For Strings -> Filter: cscapi.dll。双击查找的结果并跟踪引用,找到LoadLibrary调用。
重命名包含LoadLibrary的函数,跟踪引用可以看到两个函数地址:
我使用调用NetShareEnum和NetShareGetInfo的PoC程序验证了该过程:
Slack中,存在以下DLL劫持:
Microsoft Teams中,存在以下DLL劫持:
Visual Studio Code中,存在以下DLL劫持:
另外,我发现使用NetShareEnum和NetShareGetInfo的程序会引入cscapi.dll劫持,这是由硬编码的LoadLibrary调用导致的,并且我用Ghidra和PoC确认了该结论。
简要回顾下,DLL劫持是攻击者在签名/受信的程序中执行代码的一种方法。我开发了自动检测DLL劫持的工具。利用此工具,我发现了Slack、Microsoft Teams和Visual Studio Code中的DLL劫持。
我注意到这三个程序存在共有DLL劫持的情况,并调查了根本原因。我重点介绍了研究的方法,并了解了延迟加载DLL,以及定位到了两个API调用,这两个API会引入DLL劫持到调用它们的程序中:
感谢花时间阅读本文,希望你能学到一些关于Windows API、Ghidra、ProcMon、DLL和DLL劫持的知识!
感谢我的同事Daniel Heinsen(@hotnops), Lee Christensen(@tifkin_), and Matt Hand(@matterpreter)给我提供的帮助。
原文作者:Justin Bui
原文链接:https://posts.specterops.io/automating-dll-hijack-discovery-81c4295904b0
翻译:看雪翻译小组 SpearMint
校对:看雪翻译小组 OsWalker
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!