
Génère du shellcode indépendant de la position pour x86, x64 ou AMD64+x86 qui charge des assemblies .NET, des fichiers PE et d'autres charges utiles Windows depuis la mémoire et les exécute avec des paramètres.

Version actuelle : v1.1
Donut est un code indépendant de la position qui permet l'exécution en mémoire de fichiers VBScript, JScript, EXE, DLL et d'assemblies dotNET. Un module créé par Donut peut soit être chargé depuis un serveur HTTP, soit être intégré directement dans le chargeur lui-même. Le module est éventuellement chiffré à l'aide du chiffrement par blocs Chaskey et d'une clé générée aléatoirement de 128 bits. Une fois le fichier chargé et exécuté en mémoire, la référence originale est effacée pour dissuader les scanners mémoire. Le générateur et le chargeur prennent en charge les fonctionnalités suivantes :
Il existe des bibliothèques dynamiques et statiques pour Linux et Windows qui peuvent être intégrées dans vos propres projets. Il existe également un module Python que vous pouvez découvrir plus en détail dans la documentation sur la construction et l'utilisation de l'extension Python.
Donut contient des chargeurs individuels pour chaque type de fichier pris en charge. Pour les assemblies dotNET EXE/DLL, Donut utilise l'API Unmanaged CLR Hosting pour charger le Common Language Runtime. Une fois le CLR chargé dans le processus hôte, un nouveau domaine d'application est créé pour permettre l'exécution d'assemblies dans des AppDomains jetables. Lorsque l'AppDomain est prêt, l'assembly dotNET est chargé via la méthode AppDomain.Load_3. Enfin, le point d'entrée des EXE ou la méthode publique des DLL spécifiée par l'utilisateur est invoqué avec les paramètres supplémentaires. Référez-vous à MSDN pour la documentation sur l'API Unmanaged CLR Hosting. Pour un exemple autonome d'un hôte CLR, consultez le code ici.
Les fichiers VBScript et JScript sont exécutés à l'aide de l'interface IActiveScript. Il existe également un support minimal pour certaines des méthodes fournies par le Windows Script Host (wscript/cscript). Pour un exemple autonome, consultez le code ici. Pour une description plus détaillée, lisez : Exécution en mémoire de JavaScript, VBScript, JScript et XSL
Les fichiers EXE/DLL non managés ou natifs sont exécutés à l'aide d'un chargeur PE personnalisé avec prise en charge des importations différées, du TLS et de la correction de la ligne de commande. Seuls les fichiers avec des informations de relogement sont pris en charge. Lisez Exécution en mémoire de DLL pour plus d'informations.
Le chargeur peut désactiver AMSI et WLDP pour aider à échapper à la détection de fichiers malveillants exécutés en mémoire. Pour plus d'informations, lisez Comment les équipes rouges contournent AMSI et WLDP pour le code dynamique .NET. Il prend également en charge la décompression de fichiers en mémoire à l'aide d'aPLib ou de l'API RtlDecompressBuffer. Lisez Compression de données pour plus d'informations.
À partir de la v1.0, ETW est également contourné. Comme pour AMSI/WLDP, il s'agit d'un système modulaire qui vous permet de remplacer le contournement par défaut par le vôtre. Le contournement par défaut est dérivé des recherches de XPN. Lisez Cacher votre .NET - ETW pour plus d'informations.
Par défaut, le chargeur écrase les en-têtes PE des PE non managés (de l'adresse de base à `IMAGE_OPTIONAL_HEADER.SizeOfHeaders`). Si aucun module leurre n'est utilisé (surcharge de module), les en-têtes PE seront mis à zéro. Si un module leurre est utilisé, les en-têtes PE du module leurre seront utilisés pour écraser ceux du module de charge utile. Cela vise à dissuader la détection en comparant les en-têtes PE des modules en mémoire avec le fichier qui les supporte sur le disque. L'utilisateur peut demander que tous les en-têtes PE soient conservés dans leur état d'origine. Ceci est utile dans les scénarios où le module de charge utile doit accéder à ses en-têtes PE, par exemple lors de la recherche de ressources PE intégrées.
Pour une présentation détaillée de l'utilisation du générateur et de l'impact de Donut sur les méthodes opérationnelles, lisez Donut - Injecter des assemblies .NET en tant que shellcode. Pour plus d'informations sur le chargeur, lisez Chargement d'assemblies .NET depuis la mémoire.
Ceux qui souhaitent en savoir plus sur les aspects internes doivent consulter les notes de développement.
Il existe deux types de compilation. Si vous souhaitez déboguer Donut, veuillez vous référer à la documentation ici. Sinon, continuez à lire pour la compilation de version.
Depuis une invite de commandes Windows ou un terminal Linux, clonez le dépôt.
git clone http://github.com/thewover/donut.git
L'étape suivante dépend de votre système d'exploitation et du compilateur que vous décidez d'utiliser. Actuellement, le générateur et le modèle de chargeur de Donut peuvent être compilés avec succès avec Microsoft Visual Studio 2019 et MinGW-64. Pour utiliser les bibliothèques dans votre propre projet C/C++, veuillez vous référer aux exemples fournis ici.
Pour générer le modèle de chargeur, la bibliothèque dynamique donut.dll, la bibliothèque statique donut.lib et le générateur donut.exe. Démarrez une invite de commandes développeur Microsoft Visual Studio x64, placez-vous dans le répertoire où vous avez cloné le dépôt Donut et entrez la commande suivante :
nmake -f Makefile.msvc
Pour faire la même chose, mais en utilisant MinGW-64 sur Windows ou Linux, placez-vous dans le répertoire où vous avez cloné le dépôt Donut et entrez la commande suivante :
make -f Makefile.mingw
Pour générer la bibliothèque dynamique donut.so, la bibliothèque statique donut.a et le générateur donut. Placez-vous dans le répertoire où vous avez cloné le dépôt Donut et tapez simplement make.
Donut peut être installé et utilisé en tant que module Python. Pour installer à partir des sources, il faut pip pour Python3. Tout d'abord, assurez-vous que les anciennes versions de donut-shellcode ne sont pas installées en exécutant la commande suivante sur un terminal Linux ou une invite de commandes Microsoft Visual Studio.
pip3 uninstall donut-shellcode
Après avoir confirmé que les anciennes versions ne sont plus installées, exécutez la commande suivante.
pip3 install .
Vous pouvez également installer Donut en tant que module Python en le récupérant depuis le dépôt PyPi.
pip3 install donut-shellcode
Pour plus d'informations, veuillez vous référer à la documentation sur la construction et l'utilisation de l'extension Python.
Construction du conteneur Docker.
docker build -t donut .
Exécution de donut.
docker run -it --rm -v "${PWD}:/workdir" donut -h
Donut inclut plusieurs autres exécutables qui peuvent être compilés séparément. Cela inclut "hash.exe", "encrypt.exe", "inject.exe" et "inject_local.exe". Les deux premiers sont utilisés dans la génération de shellcode. Les deux derniers sont fournis pour aider à tester le shellcode Donut. "inject.exe" injecte un fichier binaire brut (loader.bin) dans un processus par son PID ou son nom de processus. "inject_local.exe" injecte un fichier binaire brut dans son propre processus.
Pour compiler ces exécutables de support séparément, vous pouvez utiliser le makefile MSVC. Par exemple, pour compiler "inject_local.exe" afin de tester votre shellcode Donut, vous pouvez exécuter :
nmake inject_local -f Makefile.msvc
Des tags ont été fournis pour chaque version de Donut contenant les exécutables compilés.
Actuellement, deux autres générateurs sont disponibles.
Le tableau suivant répertorie les commutateurs pris en charge par la version en ligne de commande du générateur.
Il existe certaines exigences spécifiques que votre charge utile doit respecter pour que Donut puisse la charger avec succès.
Les exécutables Cygwin utilisent des routines d'initialisation qui attendent que le processus hôte s'exécute à partir du disque. En cas d'exécution depuis la mémoire, le processus hôte risque de planter.
Quatre projets compagnons sont fournis avec Donut :
Vous pouvez souhaiter ajouter la prise en charge de davantage de types de charges utiles, modifier notre ensemble de fonctionnalités ou intégrer Donut dans vos outils existants. Nous avons fourni une documentation pour développeurs. Les fonctionnalités supplémentaires sont laissées comme exercices pour le lecteur. Nos suggestions :
Si vous avez des questions ou des commentaires sur Donut, rejoignez le canal #Donut sur le Slack de BloodHound Gang
Nous ne sommes pas responsables de toute utilisation abusive de ce logiciel ou de cette technique. Donut est fourni comme une démonstration de l'injection CLR et du chargement en mémoire via un shellcode, afin de fournir aux équipes rouges un moyen d'émuler les adversaires et aux défenseurs un cadre de référence pour construire des analyses et des mesures de protection. Cela comporte inévitablement le risque que des auteurs de logiciels malveillants et des acteurs de menaces en fassent un usage abusif. Cependant, nous pensons que le bénéfice net l'emporte sur le risque. Espérons que cela soit correct. Dans le cas où les produits EDR ou AV seraient capables de détecter Donut via des signatures ou des modèles comportementaux, nous ne mettrons pas à jour Donut pour contrer les signatures ou les méthodes de détection. Pour éviter d'être offensé, veuillez ne pas demander.
| Option | Argument | Description |
|---|---|---|
| -a | arch | Architecture cible pour le chargeur : 1=x86, 2=amd64, 3=x86+amd64 (par défaut). |
| -b | level | Comportement pour le contournement d'AMSI/WLDP : 1=Aucun, 2=Abandon en cas d'échec, 3=Continuer en cas d'échec (par défaut). |
| -k | headers | Conserver les en-têtes PE. 1=Écraser (par défaut), 2=Tout conserver |
| -j | decoy | Chemin facultatif d'un module leurre pour la surcharge de module. |
| -c | class | Nom de classe facultatif. (obligatoire pour les DLL .NET) Peut également inclure un espace de noms : par ex. espace.noms.classe |
| -d | name | Nom de l'AppDomain à créer pour .NET. Si l'entropie est activée, un nom sera généré aléatoirement. |
| -e | level | Niveau d'entropie. 1=Aucune, 2=Générer des noms aléatoires, 3=Générer des noms aléatoires + utiliser le chiffrement symétrique (par défaut) |
| -f | format | Format de sortie du chargeur enregistré dans le fichier. 1=Binaire (par défaut), 2=Base64, 3=C, 4=Ruby, 5=Python, 6=PowerShell, 7=C#, 8=Hexadecimal |
| -m | name | Méthode ou fonction facultative pour la DLL. (une méthode est obligatoire pour les DLL .NET) |
| -n | name | Nom du module pour le staging HTTP. Si l'entropie est activée, un nom est généré aléatoirement. |
| -o | path | Spécifie où Donut doit enregistrer le chargeur. La valeur par défaut est "loader.bin" dans le répertoire actuel. |
| -p | parameters | Paramètres/ligne de commande facultatifs entre guillemets pour la méthode/fonction DLL ou l'EXE. |
| -r | version | Version du runtime CLR. MetaHeader utilisé par défaut ou v4.0.30319 si aucun n'est disponible. |
| -s | server | URL du serveur HTTP qui hébergera un module Donut. Les informations d'identification peuvent être fournies dans le format suivant : https://nomutilisateur:[email protected]/ |
| -t | Exécuter le point d'entrée d'un EXE non managé/natif en tant que thread et attendre la fin du thread. | |
| -w | La ligne de commande est transmise à la fonction DLL non managée au format UNICODE. (la valeur par défaut est ANSI) | |
| -x | option | Détermine comment le chargeur doit se terminer. 1=Terminer le thread (par défaut), 2=Terminer le processus, 3=Ne pas terminer ni nettoyer et bloquer indéfiniment |
| -y | addr | Crée un nouveau thread pour le chargeur et continue l'exécution à une adresse qui est un décalage relatif à l'exécutable du processus hôte. La valeur fournie est le décalage. Cette option prend en charge les chargeurs qui souhaitent reprendre l'exécution du processus hôte après la fin de l'exécution de Donut. |
| -z | engine | Compresser/Emballer le fichier d'entrée. 1=Aucun, 2=aPLib, 3=LZNT1, 4=Xpress, 5=Xpress Huffman. Actuellement, les trois derniers ne sont pris en charge que sur Windows. |
| Outil | Description |
|---|---|
| DemoCreateProcess | Un exemple d'assembly .NET à utiliser dans les tests. Prend deux paramètres en ligne de commande qui spécifient chacun un programme à exécuter. |
| DonutTest | Un injecteur de shellcode C# simple à utiliser pour tester Donut. Le shellcode doit être encodé en base64 et copié en tant que chaîne. |
| ModuleMonitor | Un outil de preuve de concept qui détecte l'injection CLR telle qu'elle est effectuée par des outils comme Donut et Cobalt Strike execute-assembly. |
| ProcessManager | Un outil de découverte de processus que les opérateurs offensifs peuvent utiliser pour déterminer dans quoi injecter et les opérateurs défensifs peuvent utiliser pour déterminer ce qui est en cours d'exécution, les propriétés de ces processus et s'ils ont chargé le CLR ou non. |