
Crypteur polymorphe qui transforme les fichiers shellcode, PE et COFF en charges utiles obscurcies et indépendantes de la position avec chiffrement RC4 et par blocs aléatoires, ainsi qu'une génération d'instructions poubelles à l'aide de la bibliothèque AsmJit.
Présenté à
Shoggoth est un projet open source basé sur C++ et la bibliothèque asmjit, utilisé pour chiffrer de manière polymorphe un shellcode, un fichier PE ou COFF donné.
Shoggoth génère un fichier de sortie qui stocke la charge utile et son chargeur correspondant sous une forme obscurcie. Comme le contenu de la sortie est indépendant de la position, il peut être exécuté directement en tant que shellcode. Pendant l'exécution de la charge utile, elle se déchiffre elle-même à l'exécution. En plus de la routine de chiffrement, Shoggoth ajoute également des instructions poubelles, qui ne changent rien, entre les routines.
J'ai commencé à développer ce projet pour étudier différentes approches de génération dynamique d'instructions, les pratiques d'assembly et les détections de signatures. Je prévois de mettre à jour régulièrement le dépôt avec mes nouveaux apprentissages.
Les fonctionnalités actuelles sont listées ci-dessous :
Le flux d'exécution général de Shoggoth pour un fichier d'entrée est visible dans l'image ci-dessous. Vous pouvez observer ce flux avec les configurations par défaut.
En gros, Shoggoth fusionne d'abord le shellcode du chargeur précompilé selon le mode choisi (fichier COFF ou PE) avec le fichier d'entrée. Il ajoute ensuite plusieurs instructions poubelles qu'il génère à cette charge utile fusionnée. Le stub contenant le chargeur, les instructions poubelles et la charge utile est d'abord chiffré avec RC4, puis avec un chiffrement par bloc généré aléatoirement, en combinant les déchiffreurs correspondants. Enfin, il ajoute une instruction poubelle au bloc résultant.
Lorsque Shoggoth génère aléatoirement des instructions pour les stubs poubelles ou les routines de chiffrement, il utilise la bibliothèque AsmJit.
AsmJit est une bibliothèque légère pour la génération de code machine écrite en C++. Elle peut générer du code machine pour les architectures X86, X86_64 et AArch64, et prend en charge les instructions de base ainsi que toutes les extensions récentes. AsmJit permet de spécifier des codes opération, des registres, des opérandes immédiats, des étiquettes d'appel, et d'intégrer des valeurs arbitraires à n'importe quel décalage dans le code. Pour générer certaines instructions assembly en utilisant AsmJit, il suffit d'appeler la fonction API correspondant à l'opération assembly requise avec des valeurs d'opérandes assembly de la classe Assembler. Pour chaque appel API, AsmJit conserve les informations de code et de relocalisation dans sa structure interne CodeHolder. Après avoir appelé les fonctions API de toutes les commandes assembly à générer, sa classe JitRuntime peut être utilisée pour copier le code de CodeHolder vers la mémoire avec une permission d'exécution et le relocaliser.
Lors de la recherche d'une bibliothèque de génération de code, j'ai rencontré AsmJit et j'ai vu qu'elle était largement utilisée par de nombreux projets populaires. C'est pourquoi j'ai décidé de l'utiliser pour mes besoins. Je ne sais pas si Shoggoth est le premier projet à l'utiliser dans le contexte d'une équipe rouge, mais je pense qu'il peut servir de référence pour de futures implémentations.
Shoggoth peut être utilisé pour chiffrer des fichiers PE et COFF donnés, de sorte que les deux puissent être exécutés en tant que shellcode grâce à des chargeurs précompilés indépendants de la position. J'ai simplement utilisé la méthode C vers Shellcode pour obtenir la version PIC de chargeurs PE et COFF bien connus que j'ai modifiés pour mes anciens projets. Pour la compilation, j'ai utilisé le Makefile du projet HandleKatz, un dumper LSASS sous forme PIC.
En gros, pour obtenir un shellcode avec la technique C vers Shellcode, j'ai supprimé toutes les variables globales dans le code source du chargeur, fait en sorte que toutes les chaînes soient stockées dans la pile, et résolu les adresses des fonctions de l'API Windows en chargeant et en analysant les DLL nécessaires à l'exécution. Ensuite, j'ai déterminé le point d'entrée avec un script d'éditeur de liens et compilé le code en utilisant MinGW avec divers drapeaux de compilation. J'ai extrait la section .text du fichier exécutable généré et obtenu le shellcode du chargeur. Comme le fichier exécutable obtenu après modification du code comme ci-dessus ne contient que la section .text, le code de cette section peut être utilisé comme indépendant de la position.
Le code source de ces chargeurs peut être consulté et modifié dans les répertoires COFFLoader et PELoader. Les versions compilées de ces codes sources se trouvent dans le répertoire stub. Pour l'instant, si vous souhaitez modifier ou changer ces chargeurs, vous devez respecter les signatures et remplacer les binaires précompilés du répertoire stub.
Pour passer des arguments à un BOF, utilisez l'option '--coff-arg'. Shoggoth attend une chaîne hexadécimale préformatée contenant les arguments compressés, qui peut être créée en utilisant le script beacon_generate.py de TrustedSec. Une copie de ce script est incluse sous COFFArgGenerator/beacon_generate.py. (Merci à @Octoberfest7)
Shoggoth utilise d'abord l'un des chiffrements par flux, l'algorithme RC4, pour chiffrer la charge utile qu'il obtient. Après avoir généré aléatoirement la clé utilisée ici, il chiffre la charge utile avec cette clé. Le stub de déchiffrement, qui déchiffre la charge utile pendant l'exécution, est créé dynamiquement et assemblé à l'aide d'AsmJit. Les registres utilisés dans le stub sont sélectionnés aléatoirement pour chaque échantillon.
Je me suis référé au code de Nayuki pour l'implémentation de l'algorithme RC4 que j'utilise dans Shoggoth.