标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-2962] 作者: 大猪 发表于: [2021-12-17]
本文共 [286] 位读者顶过
一、前言在本篇文章中,将描述动态链接库(DLL)搜索顺序劫持的改建,以及攻击者可能如何将其用于Windows系统上的用户态持久性。这种技术可以对应到MITRE ATT&CK框架中的T1038:DLL搜索顺序劫持。 由于种种原因,DLL劫持对于攻击者来说很有帮助,但本文将重点介绍与自动启动应用程序结合使用的持久化用法。例如,在默认情况下,Slack和Microsoft Teams会在系统启动时运行,因此如果能将其中一个应用程序的DLL进行劫持,每当用户登录系统时,攻击者就可以持久访问其目标。 在介绍DLL、DLL搜索顺序和DLL劫持的概念之后,我们将探讨自动化DLL劫持侦查的方法。在这篇文章中,我们将介绍在Slack、Microsoft Teams和Visual Studio中如何侦查DLL劫持。 最后,我注意到,有许多DLL劫持是可以在不同应用程序之间共享的。我调查了其根本原因,发现如果应用程序没有位于C:WindowsSystem32路径下,使用特定Windows API调用的应用程序可能会存在DLL劫持的风险。 在这里,我要感谢我的同事Josiah Massari(@Airzero24),他在最初发现了一些DLL劫持事件,并在处理过程中阐述了他的方法,从而激发我研究自动化侦查的方法。
二、关于DLLDLL是一个包含代码和数据的库,可以同时由多个程序使用。 Windows应用程序可以使用某一个LoadLibrary*函数来利用DLL中的功能。应用程序可以引用为该应用程序自定义创建的DLL,也可以引用位于System32路径下的已有DLL。开发人员可以定义应用程序从System32加载DLL,以使用Windows中已实现的功能,这样就不必再自行编写特定功能。 例如,如果应用程序需要发出HTTP请求,那么开发人员就可以利用WinHTTP库(winhttp.dll),而不需要再使用原始套接字实现HTTP请求。
三、DLL搜索顺序劫持由于DLL在磁盘上以文件的形式存在,因此我们可能会产生疑问,应用程序怎么知道要从哪里加载DLL?Microsoft在这里完整记录了DLL的搜索顺序。 从Windows XP SP2开始,操作系统在默认情况下就已经启用了安全DLL搜索模式(HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerSafeDllSearchMode)。在启用安全的DLL搜索模式后,搜索顺序如下: 1、加载应用程序的目录; 2、系统目录,使用GetSystemDirectory函数获取该目录的路径; 3、16位系统目录,没有获取该目录路径的函数,但会对该目录进行搜索; 4、Windows目录,使用GetWindowsDirectory函数获取该目录的路径; 5、当前目录; 6、PATH环境变量中列出的目录,这里不包括App Paths注册表项指定的每个应用程序路径,在计算DLL搜索路径是不会使用到App Paths键。 在系统中可以包含同一个动态链接库(DLL)的多个版本。应用程序可以通过指定完整路径或使用其他机制(例如清单)来控制DLL的加载位置。 如果应用程序未指定从哪里加载DLL,那么Windows将会默认使用上述DLL搜索顺序。因此,作为攻击者来说,往往就会对DLL搜索的第一个位置(加载应用程序的目录)感兴趣。 如果应用程序开发人员希望从C:WindowsSystem32加载DLL,但在应用程序中没有明确写入,那么就会在搜索System32中的合法DLL之前,先加载应用程序目录中被植入的恶意DLL。这种恶意DLL加载方式被称为DLL劫持,攻击者以这种方式将恶意代码加载到受信任或已签名的应用程序中。
四、使用DLL劫持技术实现持久化在攻击者将恶意DLL植入到易受攻击的位置之后,就可以利用DLL劫持技术实现持久化,在存在漏洞的应用程序或服务运行时执行恶意代码。我的同事@Airzero24发现有攻击者在Microsoft OneDrive、Microsoft Teams和Slack中,使用了userenv.dll来实现DLL劫持。 攻击者针对了特定的应用程序进行攻击,因为在默认情况下,这些程序会被配置为在Windows启动时自动启动。我们可以在任务管理器中找到它们。 配置为开机时自动启动的Windows应用程序: 为了验证DLL劫持,我创建了一个DLL Shellcode加载程序,它将会启动一个Cobalt Strike Beacon。我将恶意DLL重命名为userenv.dll,并将其复制到易受攻击的应用程序的目录中。在启动该应用程序之后,我就看到了新的Beacon回调。 通过DLL劫持实现Cobalt Strike Beacon: 在进程管理器中,我们验证了易受攻击的应用程序确实已经加载了我的恶意DLL。 进程管理器显示恶意DLL已加载。 五、自动化DLL劫持侦查在我们研究已知的DLL劫持之后,我想看看是否可以找到其他的DLL劫持。 在测试过程中,我所使用的代码可以在这里找到:https://github.com/slyd0g/DLLHijackTest 5.1 案例分析:Slack在一开始,我在进程管理器(ProcMon)中使用了以下过滤器: 1、进程名称为slack.exe; 2、结果包含NOT FOUND; 3、路径以.dll结尾。
使用ProcMon过滤丢失的DLL:[出自:jiwo.org]
通过ProcMon发现潜在的DLL劫持攻击点: 使用当前的Shellcode加载程序DLL,我们不能轻松确定Slack成功加载的DLL名称。为此,我创建了一个新的DLL,该DLL使用GetModuleHandleEx和GetModuleFileName来确定加载的DLL的名称,并将其写入文本文件。 我们的下一个目标是解析CSV文件,以获得DLL路径列表,遍历这个路径列表,将测试DLL复制到指定的路径,启动目标进程,停止目标进程,并删除测试DLL。如果这个测试DLL能成功加载,就会将这个文件名写入到结果文件中。 在这一过程完成时,我们就有了一个文本文件,其中包含可以用于DLL劫持的列表。 在我的DLLHijackTest项目中,主要使用PowerShell脚本来完成所有工作。它负责接受ProcMon生成的CSV文件中包含的路径、恶意DLL路径、要启动的进程路径以及要传递给该进程的所有参数。
Get-PotentialDLLHijack参数: 在几分钟后,我检查了在“恶意”DLL中指定的文本文件中,是否包含可用的DLL劫持,并发现了针对Slack的以下劫持: PS C:UsersJohnDesktop> Get-PotentialDLLHijack -CSVPath .Logfile.CSV -MaliciousDLLPath .DLLHijackTest.dll -ProcessPath "C:UsersJohnAppDataLocalslackslack.exe"C:UsersJohnAppDataLocalslackapp-4.6.0WINSTA.dll C:UsersJohnAppDataLocalslackapp-4.6.0LINKINFO.dll C:UsersJohnAppDataLocalslackapp-4.6.0ntshrui.dll C:UsersJohnAppDataLocalslackapp-4.6.0srvcli.dll C:UsersJohnAppDataLocalslackapp-4.6.0cscapi.dll C:UsersJohnAppDataLocalslackapp-4.6.0KBDUS.DLL
5.2 案例分析:Microsoft Teams再次进行以上的步骤: 1、使用ProcMon识别出潜在的DLL劫持,并将数据导出为CSV文件; 2、确定启动进程的路径; 3、确定要传递给进程的所有参数; 4、使用适当的参数运行Get-PotentialDLLHijack.ps1。 最后发现,Microsoft Teams存在以下劫持: PS C:UsersJohnDesktop> Get-PotentialDLLHijack -CSVPath .Logfile.CSV -MaliciousDLLPath .DLLHijackTest.dll -ProcessPath "C:UsersJohnAppDataLocalMicrosoftTeamsUpdate.exe" -ProcessArguments '--processStart "Teams.exe"'C:UsersJohnAppDataLocalMicrosoftTeamscurrentWINSTA.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentLINKINFO.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentntshrui.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentsrvcli.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentcscapi.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentWindowsCodecs.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentTextInputFramework.dll
需要特别说明的是,在这里需要对PowerShell脚本进行少量修改,才能终止Teams.exe,因为我的脚本试图终止它尝试启动的进程,即Update.exe。 5.3 案例分析:Visual Studio Code重复上述过程,我发现了Visual Studio Code存在以下劫持: PS C:UsersJohnDesktop> Get-PotentialDLLHijack -CSVPath .Logfile.CSV -MaliciousDLLPath .DLLHijackTest.dll -ProcessPath "C:UsersJohnAppDataLocalProgramsMicrosoft VS CodeCode.exe"C:UsersJohnAppDataLocalProgramsMicrosoft VS CodeWINSTA.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS CodeLINKINFO.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS Codentshrui.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS Codesrvcli.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS Codecscapi.dll
5.4 共同的DLL劫持在进行上述分析之后,我们注意到,Slack、Microsoft Teams和Visual Studio Code共享了以下的DLL劫持:
WINSTA.dll 这非常值得关注,我们想要分析是什么导致了这样的情况。
六、理解共享的DLL劫持当Slack尝试加载WINSTA.dll、LINKINFO.dll、ntshrui.dll、srvcli.dll和cscapi.dll时,我观察了堆栈跟踪。 6.1 延迟加载的DLL在加载WINSTA.dll、LINKINFO.dll、ntshrui.dll和srvcli.dll时,我注意到堆栈跟踪中存在相似之处。 Code.exe尝试加载WINSTA.dll时的堆栈跟踪:
Teams.exe尝试加载LINKINFO.dll时的堆栈跟踪: 我可以确定,这样的行为与延迟加载的DLL有关。在加载WINSTA.dll时的堆栈跟踪中,我们发现负责延迟加载的模块是wtsapi32.dll。 于是,在Ghidra中打开wtsapi32.dll,并选择“Search” -> “For Strings” -> “Filter: WINSTA.dll”,双击找到的字符串,就可以查看其在内存中的位置。
wtsapi32.dll中的“WINSTA.dll”字符串:
寻找对WINSTA.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)中修补导入函数的地址。 FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd, //Const pointer to a ImgDelayDescr struct FARPROC * ppfnIATEntry //A pointer to the slot in delay load IAT );
查找对ImgDelayDescr结构的其他引用,我们可以找到对__delayLoadHelper2的调用,该调用随后会调用ResolveDelayLoadedAPI。在这里,我已经将函数名称、类型和变量进行重命名,以使其更加易于理解。
在Ghidra中查看的__delayLoadHelper2和ResolveDelayLoadedAPI: ProcMon中的__delayLoadHelper2和ResolveDelayLoadedAPI: 在WINSTA.dll、LINKINFO.dll、ntshrui.dll和srvcli.dll之中,行为是一致的。每个延迟加载的DLL之间,主要区别在于它们的父DLL。在这三个应用程序中: wtsapi32.dll延迟加载WINSTA.dll; shell32.dll延迟加载LINKINFO.dll; LINKINFO.dll延迟加载ntshrui.dll; ntshrui.dll延迟加载srvcli.dll。 观察到了什么有趣的地方吗?似乎shell32.dll加载了LINKINFO.dll,而LINKINFO.dll又加载了ntshrui.dll,最后由ntshrui.dll加载了srvcli.dll。 6.2 NetShareGetInfo和NetShareEnum中的DLL劫持当Slack尝试加载cscapi.dll时,我观察了堆栈跟踪,看到其中有一个LoadLibraryExW调用,该调用似乎源自srvcli.dll。 加载cscapi.dll时的堆栈跟踪: 我们再次使用Ghidra打开srvcli.dll,并选择“Search” -> “For Strings” -> “Filter: cscapi.dll”,双击找到的字符串,并在跟踪引用,看能否找到预期的LoadLibrary调用。 srvcli.dll在cscapi.dll上调用LoadLibrary: 我们对包含LoadLibrary调用的函数进行重命名,并跟踪引用,最后找到了两个函数的位置:
NetShareEnum
NetShareEnum加载cscapi.dll: 通过调用NetShareEnum和NetShareGetInfo,可以对PoC程序进行验证。
NetShareEnum加载cscapi.dll: 七、结果以下DLL劫持存在于Slack中: C:UsersJohnAppDataLocalslackapp-4.6.0WINSTA.dll C:UsersJohnAppDataLocalslackapp-4.6.0LINKINFO.dll C:UsersJohnAppDataLocalslackapp-4.6.0ntshrui.dll C:UsersJohnAppDataLocalslackapp-4.6.0srvcli.dll C:UsersJohnAppDataLocalslackapp-4.6.0cscapi.dll C:UsersJohnAppDataLocalslackapp-4.6.0KBDUS.DLL
以下DLL劫持存在于Microsoft Teams中: C:UsersJohnAppDataLocalMicrosoftTeamscurrentWINSTA.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentLINKINFO.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentntshrui.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentsrvcli.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentcscapi.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentWindowsCodecs.dll C:UsersJohnAppDataLocalMicrosoftTeamscurrentTextInputFramework.dll
以下DLL劫持存在于Visual Studio Code中: C:UsersJohnAppDataLocalProgramsMicrosoft VS CodeWINSTA.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS CodeLINKINFO.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS Codentshrui.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS Codesrvcli.dll C:UsersJohnAppDataLocalProgramsMicrosoft VS Codecscapi.dll
此外,我发现使用NetShareEnum和NetShareGetInfo的程序,由于其中包含硬编码的LoadLibrary调用,因此以cscapi.dll的形式引入了DLL劫持。通过Ghidra和PoC,我们最终得以确认了这种行为。
八、总结概括来说,DLL劫持是攻击者在已签名、受信任的应用程序中实现代码执行的一种方式。为防范此风险,我编写了一个工具来帮助自动化发现DLL劫持。使用该工具,我们成功发现了Slack、Microsoft Teams、Visual Studio Code中存在的DLL劫持风险。 我注意到这三个应用程序与其DLL劫持存在重叠的地方,并调查了根本原因。在研究过程中,我重点介绍了我的研究方法。最终,我理解了延迟加载DLL的原理,并从中确定了两个API调用,正是这两个API调用(NetShareEnum加载cscapi.dll和NetShareGetInfo加载cscapi.dll)将DLL劫持引入了所有调用它们的程序之中。 感谢大家抽出宝贵的时间来阅读这篇文章,希望通过本文,能让大家对于Windows API、Ghidra、ProcMon、DLL和DLL劫持都能有所了解。
九、致谢非常感谢我的同事Daniel Heinsen(@hotnops)、Lee Christensen(@tifkin_)和Matt Hand(@matterpreter),他们帮助我解决了Ghidra和ProcMon中遇到的一些问题。 |