English | 中文
Before using Gonut for the first time, you need to understand: "What is Donut?"
Donut is a position-independent code that enables in-memory execution of VBScript, JScript, EXE, DLL files and dotNET assemblies. A module created by Donut can either be staged from a HTTP server or embedded directly in the loader itself. The module is optionally encrypted using the Chaskey block cipher and a 128-bit randomly generated key. After the file is loaded and executed in memory, the original reference is erased to deter memory scanners. The generator and loader support the following features:
- Compression of input files with aPLib and LZNT1, Xpress, Xpress Huffman via RtlCompressBuffer.
- Using entropy for API hashes and generation of strings.
- 128-bit symmetric encryption of files.
- Overwriting native PE headers.
- Storing native PEs in MEM_IMAGE memory.
- Patching Antimalware Scan Interface (AMSI) and Windows Lockdown Policy (WLDP).
- Patching Event Tracing for Windows (ETW).
- Patching command line for EXE files.
- Patching exit-related API to avoid termination of host process.
- Multiple output formats: C, Ruby, Python, PowerShell, Base64, C#, Hexadecimal, and UUID string.
There are dynamic and static libraries for both Linux and Windows that can be integrated into your own projects. There's also a python module which you can read more about in Building and using the Python extension.
Note: Support for Xpress Huffman was temporarily removed in Donut v1.0.
To understand in more detail what Donut
is and how Donut
works, you can visit: https://github.com/TheWover/donut
The Generator compresses and encrypts files of formats such as VBScript, JScript, EXE, DLL files, and .NET assemblies based on input parameters, concatenates them with the shellcode of the Loader, and finally generates the final shellcode to be used.
The Loader is essentially a shellcode template. When executed in memory, it will decrypt and decompress the original payload (vbs, js, exe, dll, etc.) into memory and execute the payload according to the parameters provided by the Generator.
Bypassing operations such as AMSI, WLDP, ETW are executed by the Loader.
If you have correctly understood most of the content mentioned earlier, you don't need to pay attention to this section. Otherwise, you can simply regard Donut as a PE-to-Shellcode tool, which has additional features such as encryption, compression, bypassing AMSI, WLDP, ETW, stealthy invocation of system APIs compared to other tools (such as pe_to_shellcode, etc.), and can better evade AV, EDR's memory detection and behavior detection.
Gonut is a cross-platform implementation of Donut's Generator, written in pure Go without CGO, and supports most mainstream systems (Windows, Linux, macOS, etc.) and architectures (i386, amd64, arm, Apple silicon, etc.).
Note again: Gonut is just a cross-platform implementation of Donut's Generator and does not include Donut's Loader.
- Donut's Generator can only run on Windows and Linux systems.
- The behavior of Donut's Generator under Linux is not completely consistent with that under Windows. (#45)
- Donut's Generator does not support Xpress, LZNT1 compression under Linux.
- Currently, it is impossible to compile Donut under Arm architecture on Windows and Linux.
- Donut's Generator cannot be used under macOS (M-series chips).
- Let more people know about Donut, a seriously underrated project.
To solve the above problems, Gonut was born.
- Have behavior consistent with Donut on Windows across all systems (Windows, Linux, macOS, etc.) except for the compression feature (for more details, see the Differences between Gonut and Donut's Generator).
- Provide a better user experience.
- Since Gonut is completely dependent on Donut's Loader, it will not add features that Donut does not have (or explicitly states it does not support).
Donut recommends two third-party implementations of Generators:
However, these two projects have not been updated for a long time, do not support the latest version (v1.0) of Donut Loader, and do not support common functions such as Decoy, ETW Bypass, compression, specified output formats, etc.
- Difference in compression function: Since Donut uses the non-open source aPLib compression function and Microsoft's RtlCompressBuffer function, both of which cannot be perfectly reproduced on non-Windows systems, currently Gonut can only try to simulate these two compression algorithms and compression formats.
- Added output formats: Golang, Rust, etc.
For various reasons, Gonut currently does not intend to provide precompiled binary files, which means that if you want to use Gonut, you will need to install the most basic Golang development environment or Docker runtime environment.
git clone https://github.com/wabzsy/gonut
cd gonut
docker build -t gonut .
# docker run --rm -it -v `pwd`:/opt gonut -h
go install -v github.com/wabzsy/gonut/gonut@latest
git clone https://github.com/wabzsy/gonut
cd gonut/gonut
go build -v
Much the same as Donut's usage. The following table lists switches supported by the command line version of the Gonut:
Switch | Argument type | Description |
---|---|---|
-n, --modname | string | Module name for HTTP staging. If entropy is enabled, this is generated randomly. |
-s, --server | string | Server that will host the Donut module. Credentials may be provided in the following format: https://username:[email protected]/ |
-e, --entropy | int | Entropy: 1=None 2=Use random names 3=Random names + symmetric encryption (default 3) |
-a, --arch | int | Target architecture: 1=x86 2=amd64 3=x86+amd64 (default 3) |
-o, --output | string | Output file to save loader. (default: loader.[format]) |
-f, --format | int | Output format: 1=Binary 2=Base64 3=C 4=Ruby 5=Python 6=Powershell 7=C# 8=Hex 9=UUID 10=Golang 11=Rust (default 1) |
-y, --oep | int | Create thread for loader and continue execution at <addr> supplied. (eg. 0xdeadbeef) |
-x, --exit | int | Exit behaviour: 1=Exit thread 2=Exit process 3=Do not exit or cleanup and block indefinitely (default 1) |
-c, --class | string | Optional class name. (required for .NET DLL, format: namespace.class) |
-d, --domain | string | AppDomain name to create for .NET assembly. If entropy is enabled, this is generated randomly. |
-i, --input | string | Input file to execute in-memory. |
-m, --method | string | Optional method or function for DLL. (a method is required for .NET DLL) |
-p, --args | string | Optional parameters/command line inside quotations for DLL method/function or EXE. |
-w, --unicode | Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI) |
|
-r, --runtime | string | CLR runtime version. MetaHeader used by default or v4.0.30319 if none available. |
-t, --thread | Execute the entrypoint of an unmanaged EXE as a thread. | |
-z, --compress | int | Pack/Compress file: 1=None 2=aPLib [experimental] 3=LZNT1 (RTL) [experimental, Windows only] 4=Xpress (RTL) [experimental, Windows only] 5=LZNT1 [experimental] 6=Xpress [experimental, recommended] (default 1) |
-b, --bypass | int | Bypass AMSI/WLDP/ETW: 1=None 2=Abort on fail 3=Continue on fail (default 3) |
-k, --headers | int | Preserve PE headers: 1=Overwrite 2=Keep all (default 1) |
-j, --decoy | string | Optional path of decoy module for Module Overloading. |
-v, --verbose | verbose output. (debug mode) | |
-h, --help | help for gonut | |
--version | version for gonut |
The same as Donut, see Payload Requirements for details.
The same as Donut, see Disclaimer for details.
We are not responsible for any misuse of this software or technique. Gonut is provided as a demonstration of CLR Injection and in-memory loading through shellcode in order to provide red teamers a way to emulate adversaries and defenders a frame of reference for building analytics and mitigations. This inevitably runs the risk of malware authors and threat actors misusing it. However, we believe that the net benefit outweighs the risk. Hopefully that is correct. In the event EDR or AV products are capable of detecting Gonut via signatures or behavioral patterns, we will not update Gonut to counter signatures or detection methods. To avoid being offended, please do not ask.