与HelloJackHunter并肩:揭秘WinSxS的奥秘
这篇关于Windows Side-by-Side加载的文章深入探讨了我于2023年底圣诞节和新年期间在空闲时间进行的研究。众所周知,动态链接库(DLL)侧加载/DLL劫持并不是什么新技术,Windows Side-by-Side(WinSxS)也是如此;然而,从对抗性技术的角度来看,侧加载非常有用,无论是用于建立初始访问、持久化、权限提升还是在环境中执行。我在阅读了John Carroll关于ExpLoading的精彩文章后开始了这项研究,这是一种从当前目录劫持搜索顺序的技术。当时,我也在研究WinSxS,因为有人在博客文章中提到了它,但博客缺乏任何概念验证代码,这让我陷入了自己的兔子洞。因此,很多工具和这篇博客文章诞生了;在我的研究过程中,我发现了另一位研究人员过去深入研究DLL劫持和函数导出及代理的工作:Xenov - 演讲/工具。在进行这项研究时,我利用了Aaron的代码,并用Python3重写了它,我在这里进行了分叉。尽管如此,这些工具的初衷与HelloJackHunter类似,因此重写是合理的。基于他人的工作是我们学习和改进的方式。
侧加载/劫持101
动态链接库(DLL)搜索顺序劫持,通常简称为DLL劫持,通过外部DLL利用应用程序的执行流程。通过劫持用于加载合法内容的搜索顺序,可以强制应用程序加载恶意DLL。
当易受攻击的应用程序(我在野外发现了一些非WinSxS二进制文件,包括我在2020年为Nvidia获得的CVE)设置为以提升的权限运行时,任何加载到其中的恶意DLL都会继承这些提升的权限,从而实现权限提升。应用程序的行为通常不会受到干扰,因为恶意DLL被设计为无缝加载它们替换的合法DLL,或者在DLL路径未明确定义的情况下。
这种隐蔽的DLL启动能力提供了无数机会。在Rundll32不实用的场景中,转移受信任二进制文件的执行流程,遵循“靠山吃山”(LOLBINS)原则,提供了一种从不同位置部署恶意DLL并将其注入合法进程的方法。
什么是WinSxS?
WinSxS,代表“Windows Side by Side”,是位于C:\Windows\WinSxS的一个目录,Windows在其中存储安装操作系统所需的文件以及这些文件的备份或不同版本。在Windows环境中,有大量的子文件夹和几乎每个二进制文件的副本,这使其成为利用的成熟目标。因此,许多LOLBINS可能会绕过执行策略。
WinSxS在处理更新中扮演着关键角色。当你安装更新时,系统文件的新版本会被添加到该文件夹中。这确保了应用程序的最新版本可用,有助于整体系统安全和性能。这也意味着我们可以利用合法二进制文件和所述二进制文件的多个版本用于恶意目的,暂时将DLL劫持放在一边;这意味着也有多个PowerShell和cmd.exe的副本;以下是一个来自W10虚拟机的示例:
|
|
现在,在你的环境中,这些路径可能会有所不同;因此,调查WinSxS二进制文件的版本总是值得做的,并可能为在环境中执行提供额外的选项。我在特定环境中看到的是利用替代路径绕过弱应用程序允许列表实现的能力,这些实现可能阻止C:\Windows\System32,WinSxS可能会帮助你。
将两者结合
现在我们对WinSxS和DLL劫持有了更多的了解,如何将两者结合起来形成自动化的识别和狩猎工作流程?典型的工作流程如下:
- 在WinSxS中寻找二进制文件
- 映射从$currentdir调用的DLL
- 运行HelloJackHunter并在for循环中指向DLL
寻找可用的二进制文件相对容易,只需要一些PowerShell即可开始;建议是在你自己的开发系统上进行研究,并在目标环境中复制更多内容,因为提供的单行命令都不是为了操作安全而设计的,更多的是为了突出快速获取你需要/想要的东西的路径。
映射可用二进制文件
有很多方法可以寻找二进制文件。简单地在资源管理器中搜索*.exe会给你一个GUI列表,或者使用类似everything.exe的工具可以让你复制和导出路径,或者仅仅使用PowerShell如下,并将其指向WinSxS目录:
|
|
我在Windows 10虚拟机上运行了这个命令,并生成了一个很好的可用二进制文件列表,你可以在这里下载并玩耍(https://github.com/ZephrFish/HelloJackHunter/blob/main/WinSxSBins.txt)。或者,如果你想自己运行上面的命令,我会解释这个命令的作用:
GCI -Path C:\Windows\WinSxS -Recurse -Filter *.exe:这部分命令使用GCI别名作为Get-ChildItem cmdlet。它递归地(-recurse)搜索C:\Windows\WinSxS目录和子目录中所有扩展名为.exe的文件。| Select -first 20:这部分命令使用Select-Object模块/cmdlet从先前步骤获取的文件列表中选择前20个项目;你可以删除这个选项以转储所有内容,并使用类似Out-String导出到文件。| select name,fullname,@{l='FileVersion';e={[SYSTEM.version]($_.versioninfo.fileversion)}}:再次使用Select-Object cmdlet从列表中的每个项目选择特定属性。它直接选择Name和FullName属性。此外,它使用计算表达式(@{l=‘FileVersion’;e={SYSTEM.version}})创建一个名为FileVersion的自定义属性。该表达式使用($_.versioninfo.fileversion)从每个文件中提取文件版本信息,[SYSTEM.version]将其设置为System.Version对象。| group Name:这部分命令根据它们的Name属性对项目进行分组。这意味着具有相同名称的文件将被分组在一起。| %{$_.Group | sort -descending fileVersion | select -first 1}:这部分命令使用%别名作为ForEach-Object cmdlet来迭代每组文件。在每组中,它根据FileVersion属性降序排序文件(sort -descending fileVersion),然后从每组中选择第一个文件(select -first 1)。这有效地从每组中选择版本号最高的文件。
我们可以稍微修改上面的内容,仅将路径存储到一个对象中,我们可以在DLLHiJackChecker脚本中调用;该对象如下:
|
|
虽然这仅限于仅20个二进制文件,但Select -First可以修改为在狩猎时包含任意多个。以下是我之前运行的输出,显示对象存储了许多路径:
一旦我们有了可用的列表,下一步就是大规模地寻找DLL劫持。为此,可以手动利用Process Monitor搜索运行进程中的所有DLL,这些DLL由运行的应用程序加载和调用。可以采取以下步骤:
-
启动Process Monitor,使用以下过滤器,以识别潜在易受攻击的应用程序和二进制文件。
- Result contains NOT FOUND
- Path ends with .dll
-
将运行列表导出到CSV,然后可以用一些简单的PowerShell解析CSV:
|
|
一旦获得了可劫持DLL的列表,下一步就是大规模构建恶意DLL;这就是HelloJackHunter发挥作用的地方。如果手动操作不是你的风格,你也可以尝试自动化procmon,但用PowerShell会有点棘手,我从StackOverflow/Google/Git取了一些代码,并将它们混在一起,稍微修改以在这个场景中工作:
|
|
要创建DLLHijacking.pmc,可以采取以下步骤:
- 打开ProcMon
- 配置过滤器:
- 转到Filter -> Filter…或按Ctrl+L。
- 添加一个新过滤器,设置如下:
- 字段:Operation
- 条件:is
- 值:Load Image
- 动作:Include
如果你正在狩猎非WinSxS二进制文件,那么你可能想添加额外的过滤器来排除从标准受信任目录加载的DLL,例如C:\Windows\或任何其他你认为安全的目录。
- 字段:Path
- 条件:excludes
- 值:C:\Windows\
- 动作:Exclude
- 应用过滤器。
-
设置其他选项:
- 你可以指定显示相关信息的列,如Path、Result和Detail,这些显示正在访问的特定DLL路径。
-
保存你的配置:
- 转到File -> Save Filter…。
- 将配置保存为.pmc文件,适当命名,例如DLLHijacking.pmc。
HelloJackHunter - 自动化识别
自动化总是好的,可以让狩猎生活更轻松;今天我编写并发布了一个工具:HelloJackHunter,这是对HijackHunter的文字游戏。该工具扫描可用的DLL,并利用dumpbin.exe提取导出的函数,将每个与示例消息框关联。这种映射有助于枚举可以利用的函数。此外,它支持各种方法用于函数钩子和执行。
|
|
通过工作流程,第一步是构建可用二进制文件列表,检查它们是否易受攻击,然后执行hellojackhunter以导出相关函数。作为高级概念验证,以下代码可用于证明DLL劫持;再次强调,这并不新鲜。
|
|
以上是一个简单的DllMain函数;每个case选项指定了DLL加载时采取的动作:
- DLL_PROCESS_ATTACH: 当DLL加载到进程内存时触发。这发生在进程启动时或使用LoadLibrary函数动态加载DLL时。 在这种情况下,典型用途包括初始化全局或静态数据、设置钩子,或在调用进程中加载DLL时分配所需资源。
-
DLL_THREAD_ATTACH: 当在已加载DLL的进程中创建新线程时,会发生此通知。如果DLL在进程启动时加载,每个启动的新线程都会触发此条件中的任何代码。 这通常用于分配或初始化特定于新线程的数据,例如线程本地存储。
-
DLL_THREAD_DETACH: 当线程干净退出时触发。这不一定会发生在进程终止时,因为线程可能在进程结束前结束。 这是一个清理在DLL_THREAD_ATTACH中分配的资源的机会,例如释放线程特定数据以避免内存泄漏。
-
DLL_PROCESS_DETACH: 当DLL从进程内存中卸载时发生,这可能是因为进程正在终止,或者因为DLL正在通过调用FreeLibrary动态卸载。 这种情况通常用于清理任务,例如释放共享资源、注销钩子和释放DLL使用的内存,以确保干净退出,没有资源泄漏。
各种函数关于DLL劫持并不新鲜。HelloJackHunter发挥作用的新部分是将可用函数映射到DLL执行时触发。
已知易受攻击的二进制文件
因此,从我对WinSxS的研究中,我发现了四个持续易受攻击的二进制文件和几个其他文件,但最容易演示的如下表所示。还有数百个要测试,但与其全部自己做,我宁愿与社区分享,让人们自己看看!(因为Andy搞不定Ghost及其神奇的markdown实现,你需要在表格中水平滚动)
| 二进制名称 | 路径 | DLL名称/路径 |
|---|---|---|
| ngentask.exe | C:\Windows\WinSxS\amd64_netfx4-ngentask_exe_b03f5f7f11d50a3a_4.0.15912.0_none_d5e7146d665097c0\ngentask.exe | mscorsvc.dll |
| explorer.exe | C:\Windows\WinSxS\amd64_microsoft-windows-explorer_31bf3856ad364e35_10.0.22621.3235_none_31b295f9f540d278\explorer.exe | cscapi.dll |
| aspnet_wp.exe | C:\Windows\WinSxS\amd64_netfx4-aspnet_wp_exe_b03f5f7f11d50a3a_4.0.15912.0_none_107a08446d17dcf2\aspnet_wp.exe | webengine.dll, webengine4.dll |
| aspnet_regiis.exe | c:\Windows\WinSxS\amd64_netfx4-aspnet_regiis_exe_b03f5f7f11d50a3a_4.0.15912.0_none_833013222f03235e\aspnet_regiis.exe | webengine4.dll |
每个都可以通过本地目录中的DLL进行利用,并调用以执行搜索顺序劫持中的函数。它们也作为技术性的“靠山吃山”二进制文件,因为路径名中的GUI会使每个系统的路径略有不同,但二进制文件仍然存在。要轻松找到它们,可以使用以下PowerShell:
|
|
上述命令类似于Windows上的where binary,语法在这篇文章中找到。
检测此活动
最终,研究很有趣,但如何检测此活动?嗯,我写了一个简单的Yara规则来检测从C:\Windows\WinSxS外部加载DLL的WinSxS二进制文件。该规则主要专注于匹配二进制文件中的模式,这些模式表明它正在加载DLL,特别关注这些DLL加载的路径。
|
|