导语:上篇文章,虽然我们已经知道可以重写的方法和属性,以及如何更改CLR使用的默认应用程序域管理器。今天,我们就来继续说说如何将实现System.AppDomainManager的强名称程序集安装到GAC中,并最终实现使用公共语言运行时获取持久性。
上篇文章,虽然我们已经知道可以重写的方法和属性,以及如何更改CLR使用的默认应用程序域管理器。今天,我们就来继续说说如何将实现System.AppDomainManager的强名称程序集安装到GAC中,并最终实现使用公共语言运行时获取持久性。
程序集和全局程序集缓存
如上所述,必须将实现System.AppDomainManager的强名称程序集安装到GAC中,但是,仍有很多问题,比如:
1.什么是程序集?
2.什么是全局程序集缓存(GAC)?
3.为什么以及如何将强名称程序集安装到GAC中?
对于.Net Framework,程序集是一个单元,每个程序集都可以包含MSIL代码、类型、资源和清单。所有这些数据可以分组到一个文件(例如assembly.dll)或拆分成多个文件(例如assembly.dll,assembly.dll.manifest和picture.png)。应该注意的是,只需要清单就可以了。
清单包含用于描述程序集的所有信息,例如程序集名称(例如Context.CLRHooking)、版本(例如1.0.0.0)、 语言文化(culture)属性属性属性(例如neutral)、强名称信息、程序集中所有文件的列表、类型引用信息和关于引用程序集的信息。名称、版本、语言文化(culture)属性属性属性和强名称的组合表示程序集的标识。
强名称程序集只不过是使用强名称密钥(SNK)文件签名的程序集,该文件使用公钥加密并为程序集提供唯一标识。
程序集可以设计为由一个应用程序使用,也可以在多个应用程序之间共享。在这种情况下,全局程序集缓存(GAC)会发挥作用,因为GAC会存储任何打算在整个系统范围内共享的程序集。
在Windows 10或Windows Server 2019等最新的系统中,都有两个GAC,一个位于“C:\Windows\assembly\”,用于在4.0版之前使用.Net Framework的应用程序,另一个位于“C:\Windows\Microsoft.NET\assembly”适用于使用.Net Framework 4.0及以上版本的应用程序。
有两种常规方法可以将程序集安装到GAC中;
1.安装应用程序时,Microsoft安装程序将自动将需要在GAC中的任何程序集安装到GAC中;
2.通过使用Windows软件开发工具包(SDK)中的GAC工具“gacutil.exe”。
由于目标系统不太可能安装Windows SDK,并且安装应用程序不方便,因此应采用其他解决方案。应该注意,无论使用何种技术,都需要管理权限才能将强名称程序集安装到GAC中。
虽然可以手动安装,但是,需要提前了解GAC如何存储程序集。
GAC的根文件夹包含三个文件夹,其中 'GAC_32'包含32位程序集,'GAC_64'包含64位程序集,'GAC_MSIL'包含针对任何平台的程序集。
如果强名称程序集安装到'AC_MSIL'文件夹中,则32位和64位运行时主机都可以使用它。
通过分析已安装到GAC中的强名称程序集,可以找到强名称程序集使用的全名和.Net Framework版本。 'System.Reflection.Assembly'中的'LoadFrom'静态方法用于将程序集加载并存储到变量中。从此变量中,可以访问“FullName”和“ImageRuntimeVersion”属性。
程序集的全名是 ‘Accessibility, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ ,.Net Framework版本是“v4.0.30319”。该程序集位于:‘Accessibility\v4.0_4.0.0.0__ b03f5f7f11d50a3a’。
基于以上所述,可以得出结论,当使用Microsoft安装程序或GAC工具将程序集安装到GAC中时,它们首先创建一个名为“GAC_32”,“GAC_64”或“GAC_64”下程序集名称的文件夹。 “GAC_32”,“GAC_64”或“GAC_64”都是基于编译程序集的平台。然后,创建一个子目录,该目录的名称基于程序集的标识。
格式如下:
1.“v”字符+ .Net Framework版本+“_”字符的主要版本;
2.程序集的版本+“_”字符;
3. 语言文化(culture)属性属性属性如果不是中性+“_”字;
4.程序集强名称;
例如,如果强名称程序集全名为“Context.Hook,Version = 0.0.0.0,Culture = en,PublicKeyToken = 343c81d3defacaa9”且运行时版本为“v4.0.30319”,则程序集理论上应位于:“C:\ Windows \ Microsoft.Net \ assembly \ GAC_MSIL \ Context.Hook \ v4.0_0.0.0.0_en_343c81d3defacaa9 \”。
要自动将程序集安装到GAC中,可以使用以下PowerShell脚本:https://gitlab.com/snippets/1878274。
最后,需要将程序集安装到GAC中的原因是它们需要完全受信任,但也因为运行时主机具有一种特定的方法来定位和将程序集嵌入到系统上。
实际上,CLR执行了四个步骤。首先,CLR检查应用程序配置文件、发布服务器策略文件和机器配置文件,以便定位必须加载的程序集和依赖项。其次,CLR检查运行时主机或应用程序域是否已经加载了程序集。如果已经加载,CLR将使用先前加载的程序集。第三,如果请求强名称程序集,CLR将在GAC中搜索强名称程序集。最后,如果程序集不是强名称程序集或在GAC中找不到,则CLR将探测应用程序库。
如果强名称程序集不引用语言文化(culture)属性属性属性(即中性),则CLR将探测以下目录:
[application base] / [assembly name].dll [application base] / [assembly name] / [assembly name].dll
如果引用了一种语言文化(culture)属性属性属性(例如en,en-GB,fr-FR),CLR将探测以下文件夹:
[application base] / [culture] / [assembly name].dll [application base] / [culture] / [assembly name] / [assembly name].dll
应该注意的是,如果在检查应用程序配置文件时,在步骤1中发现了一个“codeBase”元素,则CLR将在探测应用程序库之前首先检查提供的位置。
例如,以下应用程序配置文件可用于通过HTTPS加载强名称程序集:
<configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Context.CLRHooking" publicKeyToken="343c81d3defacaa9" culture="en"/> <codeBase version="1.0.0.0" href="https://www.contextis.com/Context.CLRHooking.dll"/> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
总结
说了这么多,我们应该能够理解,作为攻击者,如何利用CLR在Microsoft Windows系统上获得持久性。
具体过程分6步:
1.运行时主机总是创建一个默认的应用程序域;
2.可以使用实现System.AppDomainManager类的自定义强名称程序集重写默认应用程序域管理器;
3.必须将自定义强名称程序集安装到GAC中;
4.必须设置APPDOMAIN_MANAGER_ASM、APPDOMAIN_MANAGER_TYPE、COMPLUS_Version环境变量;
因此,如果将实现System.AppDomainManager类的强名称程序集安装到GAC中,如果将上述环境变量设置为系统环境变量,则任何新创建的应用程序域都将在强名称程序集中执行代码。
如上所述,需要构建一个强名称的程序集。我们就以以下C#代码为例:
using System; namespace Context { public sealed class CLRHooking : AppDomainManager { public override void InitializeNewDomain(AppDomainSetup appDomainInfo) { System.Windows.Forms.MessageBox.Show("Hook my CLR!"); return; } } }
首先,创建一个名为“Context”的命名空间,该命名空间仅用于组织目的。其次,名为'CLRHooking'的公共类通过':'运算符实现'System.AppDomainManager'类。请注意,该类是封闭的,这意味着其他类不能从'CLRHooking'继承。最后,'System.AppDomainManager'类中的'InitializeNewDomain'方法被重写,并在调用时显示一个消息框。
为了将上述c#代码编译成强名称程序集,需要一个SNK文件。可以使用Windows SDK中的强名称工具“sn.exe”来创建新的SNK文件,其中'-k'标志用于指定密钥长度。
最后,可以调用.Net Framework附带的C#编译器“csc.exe”将代码编译为强名称程序集。'/target:library'标志意味着代码将被编译到库(即DLL)中。对于Windows可执行文件,它将是 '/target:winexe'。 '/ keyfile:'标志用于指定将用于对程序集进行签名的SNK文件。 '/ out:'标志用于指定输出文件的名称,其中最后一个参数是要编译的C#代码。
现在可以将强名称程序集安装到GAC中,同样,也可以使用我们之前使用的PowerShell脚本。
要设置系统环境变量,可以执行以下PowerShell cmdlet。
此时,调用CLR的任何进程都将在已安装的强名称程序集的覆盖的“InitializeNewDomain”方法中执行代码,该方法将显示一个消息框。
由于任何调用CLR的过程都是模糊的,因此,必须确定找到使用CLR的进程的可靠方法。
识别基于.Net Framework的应用程序
如上所述,任何调用CLR的基于.Net Framework的应用程序都应该执行强名称程序集中的代码,但是,没有这些应用程序的列表。
由于.Net Framework使用的DLL之前已经确定,并将利用这些DLL枚举正在加载它们的应用程序。
第一种解决方案是使用“tasklist.exe”DOS命令,它显示所有当前正在运行的进程,以及使用给定DLL名称筛选进程的“/ m”标志。
如上图所示,Microsoft Office Word,explorer或PowerShell等应用程序都使用CLR的DLL,这意味着它们应该在强名称程序集中执行代码。
此解决方案的问题是'tasklist.exe'仅显示当前正在运行的进程,因此需要找到一种更好,更准确的方法来列出基于.Net Framework的应用程序。
PowerShell模块是一组基于相同目的的功能和脚本,例如逆向工程、post-exploitation, active directory enumeration 。
为此Matt Graeber(@mattifestation)发布了专门用于逆向工程的PowerShell模块,你可以点此下载。
“PowerShellArsenal”模块包含一个名为“Get-PE”的函数,可以用来解析一个PE文件的DOS标头文件,提取结构、模块名或导入DLL列表等信息以及它们的函数名称。
要使用“Get-PE”函数,必须将模块安装在以下PSModulePath中:
%Windir%\System32\WindowsPowerShell\v1.0\Modules %UserProfile%\Documents\WindowsPowerShell\Modules %ProgramFiles%\WindowsPowerShell\Modules
对于此示例,该模块将安装在“Context”用户的PowerShell模块文件夹中。
然后导入该模块,对PE文件进行分析,例如powershell.exe:
如前所述,所有导入的列表,即PE文件使用的所有DLL和函数,可以通过以下PowerShell cmdlet找到:
它再次显示了'powershell.exe'PE文件正在导入'mscoree.dll',这意味着启动一个新的PowerShell进程将在强名称程序集中执行代码。
实际上,启动新的PowerShell进程会显示一个消息框。
为了分析多个PE文件,可以改进以前的PowerShell cmdlet。例如,下面的PowerShell函数列出给定目录中的所有PE文件,然后利用“get -PE”函数获得每个PE文件的所有导入dll的列表。最后,该函数使用正则表达式检查是否导入了.Net Framework DLL。
代码可以在GitLab上找到。
function Find-DotNetFrameworkBinaries { Param( [parameter(Mandatory=$true)] [string]$Location ) Write-Host "[*] Paul Laîné (@am0nsec)" Write-Host "[*] List all binaries that are using .Net Framework`n" Try { Import-Module PowerShellArsenal } catch { Write-Host "[-] PowerShellArsenal module not found!" exit } $pes = Get-ChildItem $Location -Recurse -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Get-PE -ErrorAction SilentlyContinue -WarningAction SilentlyContinue Foreach ($pe in $pes) { $imports = ($pe.Imports | select ModuleName | Get-Unique -AsString).ModuleName Foreach ($module in $imports) { if ($module -match "^mscor(.)+\.dll" -or $module -match "^clr\.dll$") { $pe.ModuleName break } } } }
通过导入和执行上述PowerShell函数,可以在 'C:\Windows\System32\' 目录中找到以下PE文件。
然后,可以通过执行“C:\Windows\System32\FileHistory.exe”来确认这一点。
应该注意的是,许多应用程序在Windows上使用.Net Framework,还有就是“explorer.exe”,它至少在每次用户向系统进行身份验证时执行。
现在我们应该可以清楚地了解.Net内部的基础知识以及如何通过利用公共语言运行时在Microsoft Windows系统上持久化。此外,我们应该能够创建自己的强名称程序集,该程序集实现System.AppDomainManager类并在我们的系统上搜索基于.Net-Framework的应用程序。
出于讲述的目的,本文的强名称程序集仅显示一个消息框,但是,CLR的这个挂钩可以用来执行任何C#代码,例如平台调用。
附录
附录A
本文所讲的持久性技术针对以下版本的Windows进行了测试
Microsoft Windows 10 Pro(10.0.17763版本);
Microsoft Windows 10 Enterprise(10.0.17763版本);
附录B
这些PowerShell函数可用于将强名称程序集自动安装到系统的GAC中,然后相应地更改系统环境变量。