freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

技术分享 | 绕过Cobalt-Strike在使用.NET程序集时的大小限制
Alpha_h4ck 2020-12-25 12:50:53 266940

话不多说,我们直奔主题!

Cobalt-Strike的问题

Cobalt Strike是全球众多咨询公司和网络犯罪分子都会使用的命令和控制软件,Cobalt Strike能够通过生成一个新进程并将CLR(用于.NET的解释器)引导到该进程上来实现在内存中执行.NET程序集,这种功能行为通常也被称为CLR工具(技术)。这个功能非常棒,使用起来也非常好,但是它存在一个缺点,即Cobalt-Strike只能在程序集是自包含的情况下执行程序集。这意味着,如果您的程序集使用任何依赖项(比如说其他的程序集或DLL),那么就会出现问题了。不过,我们有两种方法来绕过这个限制:

使用ILMerge或Fody(或其他一些链接器)将依赖项嵌入进去;

使用反射在执行时加载依赖项(本文的主要内容);

将依赖项嵌入进去的问题就在于,这会导致程序集变得非常大,依赖项越多,体积也就越大。这也就引出了Cobalt-Strike的第二个问题:

如果程序集大于1MB,Cobalt Strike无法内存中执行它了

通常情况下,这并不是什么大问题。但是在某些特殊情况下,你的程序集可能会远远超过1MB,这就导致我们不得不想办法在运行时加载依赖项了。

.NET加载依赖项的方式

为了让我们了解如何创建在运行时加载其依赖项的程序集,首先我们要了解.NET如何加载其引用。完整的技术解析文档实际上是由微软自己编写的,但是从高层的角度来看,可以归结为如下内容:

一般来说,我们需要添加对项目的引用并在程序开始处使用using关键字来导入。一个典型的例子就是使用System.web,它可以让我们的程序集调用System.web.dll(位于GAC中-全局Assembly缓存),GAC位于%WinDir%之中,攻击者一般对它都不感兴趣,因为如果没有实现提权的话,WinDir是不能写入内容的。

如果程序集在GAC中找不到引用,它将在程序集所在的目录中查找引用。如果没有找到它,它也会出错(前提是没有配置文件,但是配置文件超出了本文的范围)。

欢迎来到“反射”的世界

绕过搜索依赖项行为的一种方法是使用反射技术,常见的反射技术实施步骤如下:

使用Load方法调用我们所要使用的程序集;

使用GetType方法调用获取到正确的Type(=类);

使用GetMethod方法调用获取到我们索要调用的实际方法;

使用CreateInstance方法调用“激活”Type(=类);

使用Invoke方法来调用目标方法;

在这种方法的帮助下,我们甚至还可以在PowerShell中调用C#程序集,这在应用程序锁定的环境中可能非常有用,因为我们仍然可以运行PowerShell。

下面给出的例子中,我们演示了如何在PowerShell中调用C#程序集:

using System;

using System.Reflection;

namespace HelloFromDotNetFramework

{

public class Program

{

 public static void Main(string[] args)

{

 Console.WriteLine("Hi from {0}", Assembly.GetExecutingAssembly());

}

}

}

clear

$bytes=[System.IO.File]::ReadAllBytes("C:\Users\jeanm\source\repos\HelloFromDotNetFrame

$asm = [System.Reflection.Assembly]::Load($bytes)

$params = {null}

[HelloFromDotNetFramework.Program]::Main($params)

执行这个PowerShell脚本之后,将会生成如下图所示的输出结果:

不过,我们还可以通过互联网获取bytearray,而不是使用PowerShell中的webclass来从本地磁盘中加载它。

尽管如此,这种方法还是存在缺点的。使用此方法的主要缺点是必须使用MethodBase.Invoke来调用,这意味着你需要调整代码以支持这种加载方式,但有可能会让你对每一个函数的调用都要进行重构和调整。

此方法的另一个缺点是,所有方法都必须是公共方法,并且一些“复杂”的操作可能会引起中断。

关于应用程序域(AppDomain)

绕过.NET程序集搜索依赖项的“正常”行为的另一种方法是使用AppDomain.AssemblyResolve方法。应用程序域能够提供安全性、可靠性和版本控制等服务,它通常由运行时主机创建,运行时主机负责在应用程序运行前引导公共语言运行时环境。

AssemblyResolve方法是一个回调函数,当应用程序域找不到引用的程序集时便会触发该函数。使用此方法可以像正常情况一样创建程序集,正常调用对依赖项的所有调用。

不过,我在测试过程中遇到了一个小问题。如果将AssemblyResolve方法放在main方法中,并且也从主方法中的依赖项进行调用,那么AssemblyResolve可能就无法正常工作了。这是因为它作为订阅服务工作,并且因为依赖项也位于main方法中,所以resolve方法还没有任何订阅。解决方法如下:

创建一个静态类并创建一个构造函数来设置这个程序集解析方法,从而在调用Main方法之前创建它。

将依赖关系逻辑放在一个单独的方法中,并从main函数中调用该方法。

下面给出的这个PoC将演示我上述介绍的内容,其中NvisoLib是一个小型的测试dll:

using System;

 

namespace NvisoLib

{

    public class Test

    {

        public void Say(String message)

        {

            Console.WriteLine(message);

        }

 

    }

}

AppDomainResolveTest是一个用于测试的可执行文件,并且使用了NvisoLib。

我们可以看到,NvisoLib的调用过程就像你通常会利用任何其他依赖性一样,我把逻辑放在seperate函数中,并从main函数中调用它。我用一个非常简单的实现实现了RevolveAssembly函数,并从磁盘上的另一个位置来调用它。

总结

在这篇文章中,我们探讨了如何利用反射技术在运行时加载依赖项,从而避免了将所有的依赖组件都嵌入到一个程序集之中,这样就保证了不超过Cobalt-Strike给出的1MB大小限制。除此之外,我们还介绍了如何通过反射技术在PowerShell中执行C#程序集。

# Cobalt strike # .Net # Cobalt Strike
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 Alpha_h4ck 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Alpha_h4ck LV.10
好好学习,天天向上
  • 2359 文章数
  • 1024 关注者
Tetragon:一款基于eBPF的运行时环境安全监控工具
2025-01-21
DroneXtract:一款针对无人机的网络安全数字取证工具
2025-01-21
CNAPPgoat:一款针对云环境的安全实践靶场
2025-01-21
文章目录