Si les composants d’IA et de machine learning de nouvelle génération des solutions de sécurité continuent d’améliorer les capacités de détection comportementales, beaucoup reposent encore sur la détection basée sur la signature. Cobalt Strike étant un cadre de commandement et de contrôle (C2) très utilisé par les acteurs de la menace et les équipes rouges depuis ses débuts, il continue à être fortement signé par les solutions de sécurité.
Pour poursuivre l’utilisation opérationnelle de Cobalt Strike, nous, membres de l’équipe IBM X-Force Red Adversary Simulation, avons déployé d’importants efforts de recherche et développement pour personnaliser Cobalt Strike avec des outils internes. Certains de nos outils internes spécifiques à Cobalt Strike ont de versions publiques telles que « InlineExecute-Assembly », « CredBandit » et « BokuLoader ». Ces deux dernières années, étant donné le nombre excessif de signatures Cobalt Strike, nous limitons son utilisation à la simulation d’acteurs de la menace moins sophistiqués, et nous privilégions d’autres C2 tiers et internes pour effectuer des exercices d’équipe rouge plus avancés.
Grâce à nos efforts de recherche et développement, nous avons obtenu de meilleurs résultats opérationnels lors des exercices avancés en équipe rouge avec :
Cependant, de nombreux acteurs malveillants exploitent toujours des copies piratées de Cobalt Strike, et il reste important de pouvoir simuler leur activité. Les équipes rouges prêtes à déployer des efforts de recherche et de développement peuvent encore réussir leurs opérations avec Cobalt Strike, tout en simulant ces adversaires. En outre, Cobalt Strike est un excellent outil d’apprentissage, qui peut être utilisé par les novices pour acquérir de l’expérience avec un cadre C2 par le biais de cours de formation en équipe rouge.
Alors que nous continuons à développer nos capacités C2, nous partageons quelques informations sur la manière dont nous nous sommes appuyés sur le cadre Cobalt Strike par le passé, notamment en développant des chargeurs réflexifs sur mesure. Il souhaitons également aider les défenseurs de comprendre le fonctionnement de Cobalt Strike afin de créer des détections plus robustes.
Restez au fait des tendances les plus étonnantes du secteur dans le domaine de l’IA, de l’automatisation, des données et bien d’autres avec la newsletter Think. Consultez la Déclaration de confidentialité d’IBM.
Cet article de blog est le premier d’une série qui sert d’introduction, abordant les bases du développement de chargeurs réflexifs Cobalt Strike. Au fil de cette série, nous nous appuierons sur ces bases et ferons référence à cet article.
À l’issue de cette série, nous visons à créer un chargeur réflexif qui s’intègre aux fonctionnalités d’évitement existantes de Cobalt Strike, voire qui les améliore à l’aide de techniques avancées actuellement absentes de l’outil. Les prochains articles aborderont en détail le développement de différentes fonctionnalités d’évitement et la manière de les intégrer dans notre chargeur réflexif Cobalt Strike.
Pour commencer, cet article abordera les points suivants :
En explorant le chargement réflexif de Cobalt Strike du point de vue d’un développeur d’outils de sécurité offensifs, nous soulignerons les opportunités de détection et d’évitement. Certains aspects du développement seront omis ou simplifiés, et nous vous encourageons à combler les lacunes en déboguant les projets de chargeurs réflexifs existants, en les reconstruisant à partir de zéro ou en suivant une formation.
L’implant Cobalt Strike C2, connu sous le nom de Beacon, est une bibliothèque DLL Windows, et la capacité modulaire d’utiliser notre propre chargeur DLL dans Cobalt Strike est connue sous le nom de User-Defined Reflective Loader (UDRL).
Le chargeur DLL intégré de Windows est généralement responsable du chargement DLL dans l’espace mémoire virtuel d’un processus. Le chargeur DLL Windows se trouve principalement dans l’espace utilisateur, bien qu’il passe dans l’espace du noyau lors du mappage de DLL à partir d’un disque.
L’utilisation du chargeur DLL Windows présente quelques inconvénients lorsqu’il est utilisé pour simuler les adversaires :
Par conséquent, utiliser le chargeur DLL Windows pour charger notre DLL Beacon n’est pas une solution idéale. Pour surmonter ces défis, nous chargeons la DLL Beacon à partir de la mémoire à l’aide d’un chargeur réflexif.
Les trois principaux points de détection évités par le chargement réflexif sont les suivants :
Le chargement réflexif consiste à tout simplement charger une DLL brute directement à partir de la mémoire, au lieu de la charger à partir du système de fichiers.
Le chargeur réflexif et le chargeur DLL intégré à Windows permettent tous deux de charger une DLL à partir d’un format de fichier brut dans l’espace mémoire virtuelle d’un processus. Cependant, le chargement réflexif présente un avantage majeur par rapport au chargeur DLL Windows? En effet, il ne requiert pas que le fichier DLL existe sur le système de fichiers. Ce chargement en mémoire permet un nombre illimité de phases de chargement en chaîne, car la DLL de l’implant C2 peut être cachée dans les couches de chiffrement et d’encodage de la mémoire du processus.
Un aspect clé à connaître lors du chargement d’une DLL est celui que la DLL sera formatée différemment sur disque et en mémoire. Les principales différences entre la DLL au format de fichier brut et au format d’adresse virtuelle sont les suivantes :
Format de fichier brut :
Format d’adresse virtuelle :
En examinant une DLL beacon HTTP dans l’outil PE-Bear d’Aleksandra Doniec, nous constatons des différences entre l’adressage brut et virtuel pour chaque section de la DLL :
Tableau listant les adresses brutes et virtuelles de chaque section de la DLL beacon.
Cette DLL beacon HTTP/S est
PE-Bear fournit une représentation visuelle de notre DLL beacon telle qu’elle existe au format de fichier brut et au format d’espace d’adresse virtuelle :
Représentation visuelle de la DLL beacon au format brut (à gauche) et au format virtuel (à droite)
Bien que ce ne soit pas le choix le plus judicieux lors d’une simulation d’adversaire, déposer sur le disque une DLL beacon brute sans brouillage et la charger à l’aide du chargeur DLL Windows permet de démystifier le chargement beacon et DLL. En fait, Beacon n’est rien d’autre qu’une DLL. Le chargeur DLL Windows et le chargeur réflexif chargent tout simplement une DLL dans un processus.
Pour charger la DLL Beacon avec le chargeur DLL Windows, nous procédons comme suit :
LoadLibrary
API pour charger notre DLL Beacon à partir du disque.Tout d’abord, nous désactivons toutes les options Malleable PE qui font que notre DLL beacon ne peut pas être chargée par le chargeur DLL Windows. Pour ce faire, nous modifions notre profil Malleable C2 et désactivons les options d’évitement de Malleable PE situées dans le bloc stage :
Bloc stage de profil Malleable C2 modifié pour désactiver les fonctionnalités d’évitement de Cobalt Strike.
Après avoir modifié le profil, nous redémarrons le serveur Cobalt Strike Team, en fournissant notre
Nous nous connectons au serveur Team via le client Cobalt Strike. Ensuite, nous créons notre
Capture d’écran illustrant la création d’une DLL beacon « brute sans mise en scène » à partir du client Cobalt Strike
En utilisant le code ci-dessous, nous créons un programme en C nommé
Code C Windows pour charger la DLL beacon à partir du disque à l’aide du chargeur DLL Windows.
Nous utilisons
Durant le processus de chargement, le chargeur DLL Windows initialisera notre DLL beacon en appelant son point d’entrée avec
Une fois que le chargeur DLL Windows a chargé et initialisé notre DLL beacon dans l’espace mémoire virtuel de notre processus, nous devons à nouveau appeler le point d’entrée de la DLL beacon virtuelle avec l’argument
Notre programme doit connaître le point d’entrée de notre DLL beacon virtuelle pour l’exécuter. Cela peut être fait de manière dynamique dans le programme en analysant les en-têtes de la DLL beacon virtuelle pour identifier l’adresse virtuelle relative (RVA) du point d’entrée, ou nous pouvons rapidement l’examiner et coder la valeur en dur.
Pour notre preuve de concept, nous découvrirons manuellement et coderons en dur la RVA du point d’entrée de notre DLL beacon dans notre programme. En utilisant PE-Bear, nous découvrons que la RVA vers le point d’entrée du beacon est
Capture d’écran illustrant la recherche de la RVA du point d’entrée de la DLL beacon avec PE-Bear
Le
Notre code étant prêt, nous compilons notre programme C en exécutable Windows :
Commande utilisée pour compiler notre programme.
En plaçant notre DLL beacon et notre programme de chargement beacon exécutable dans le même répertoire, nous permettons au chargeur DLL Windows de découvrir notre DLL au fur et à mesure qu’elle effectue sa routine de chargement.
Nous plaçons les deux
La DLL Beacon et le programme de chargement sont placés dans le même répertoire.
Dans notre bureau Windows, nous double-cliquons sur le programme loadBeaconDLL.exe et établissons une connexion beacon active à notre Team Server.
Connexion réussie à C2 Team Server à partir de la DLL beacon chargée à l’aide du chargeur DLL Windows.
Cobalt Strike utilise une version modifiée du projet Reflective Loader de Stephen Fewer. Ce légendaire chargeur de DLL en mémoire, qui a plus de dix ans, a été utilisé dans Metasploit et d’autres outils de sécurité offensifs notables.
Au fil des années, le chargeur réflexif Cobalt Strike a été amélioré pour prendre en charge toutes les fonctionnalités d’évitement Malleable PE qu’offre Cobalt Strike. Le principal inconvénient lié à l’utilisation d’un chargeur réflexif défini par l’utilisateur (UDRL) est que les fonctionnalités d’évitement Malleable PE peuvent ou non être prises en charge telles quelles.
Certaines fonctions d’évitement sont entièrement implémentées lors de l’utilisation d’un UDRL, étant intégrées à la DLL Beacon par le moteur Malleable PE de Cobalt Strike lors de la création de la charge utile Beacon. Actuellement, des fonctionnalités comme doivent être gérées par l’UDRL, tandis que d’autres comme et peuvent être traitées par Beacon avec une intégration UDRL appropriée.
Le projet d’origine Reflective Loader exige de compiler
Ensuite, un autre projet est chargé de :
Diagramme du chargeur réflexif d’origine chargeant une DLL dans la mémoire virtuelle.
Une autre méthode consiste à ajouter le chargeur réflexif à la DLL. Cela permet de charger n’importe quelle DLL non gérée et n’exige pas de compiler la DLL à partir du code source. Il s’agit d’une méthode de chargement réflexif robuste, capable de charger n’importe quel fichier PE (EXE ou DLL).
Diagramme d’un chargeur réflexif ajouté à une DLL, chargeant une DLL dans la mémoire virtuelle.
La mise en œuvre du chargement réflexif par Cobalt Strike associe les deux méthodes susmentionnées. Cette méthode de chargement réflexif peut être familière à ceux qui savent comment le Meterpreter de Metasploit réalise le chargement réflexif.
Comme avec la méthode du chargeur réflexif d’origine,
Lorsqu’un UDRL est chargé dans Cobalt Strike et qu’un opérateur génère une charge utile beacon à partir du client Cobalt Strike, le moteur Malleable PE de Cobalt Strike corrige le shellcode du chargeur réflexif à l’offset du fichier brut de
Lorsque le moteur Malleable PE corrige la DLL beacon brute, cette dernière est donnée à l’opérateur dans un format exécutable similaire à un shellcode.
Diagramme du chargeur réflexif Cobalt Strike chargeant la DLL beacon dans la mémoire virtuelle.
En examinant les octets initiaux dans le désassembleur PE-Bear, nous constatons que la DLL beacon est elle-même exécutable :
Le stub d’appel du chargeur réfléchissant montré sous forme de codes d’opération d’assemblage exécutables.
Les octets initiaux
Après l’exécution des octets éventuellement préfixés
Nous confirmons que l’offset du fichier brut pour
Capture d’écran illustrant l’utilisation de PE-Bear pour déterminer l’offset du fichier brut de l’exportation ReflectiveLoader.
Telle qu’elle existe dans le répertoire d’exportation, l’adresse pour
Pour connaître l’offset du fichier brut de
Les adresses virtuelle et brute
Adresses brutes et virtuelles de la section .text de la DLL beacon.
La différence entre les deux est
Nous le confirmons dans PE-Bear en faisant un clic droit sur
En résumé, le processus de chargement réflexif de Cobalt Strike est le suivant :
Diagramme illustrant les principales phases du chargement réflexif de DLL beacon par Cobalt Strike.
Étant donné que notre chargeur réflexif est exécuté avant le chargement de la DLL beacon, le code du chargeur réflexif doit être un shellcode pur.
La manière la plus simple de créer un shellcode complexe est de l’écrire en C, sans aucune dépendance externe. Le fichier C est ensuite compilé en fichier objet. Tout doit être inclus dans la section du fichier objet. Enfin, nous retirons les.text
Le moteur Malleable PE de Cobalt Strike se chargera de récupérer le shellcode du fichier objet de notre chargeur réflexif et de l’ajouter à la DLL beacon brute à l’offset du fichier brut de
Script Aggressor pour écrire le shellcode du chargeur réflexif dans la DLL beacon brute en s’appuyant sur Cobalt Strike.
Notre script UDRL Aggressor demande à Cobalt Strike d’écrire le shellcode de notre chargeur réflexif en effectuant les étapes suivantes :
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#extract_reflective_loader">extract_reflective_loader</a>Cobalt Strike Aggressor intégrée analysera notre fichier d’objets UDRL à partir du
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#setup_reflective_loader">setup_reflective_loader</a>La fonction Cobalt Strike Aggressor utilisera le moteur Malleable PE pour découvrir l’offset du fichier brut de notre
Cobalt Strike a réalisé pour nous l’extraction de la section .text du fichier d’objets de notre chargeur réflexif, l’ajout du shellcode de notre chargeur réflexif et l’appel de notre chargeur réflexif avec le stub d’appel du chargeur réflexif situé dans l’en-tête de la DLL beacon.
Voici les phases que nous devons développer pour charger le beacon de manière réflexive :
Il existe plusieurs méthodes pour découvrir l’adresse de la DLL beacon brute en mémoire. En voici quelques-unes :
Lorsque nous utilisons une méthode de chasse à rebours, nous devons d’abord obtenir l’adresse actuelle du pointeur d’instruction de notre thread (
Code d’assemblage Intel x64 pour obtenir l’adresse de base de la DLL beacon brute à partir du registre RDI.
Le projet de chargeur réflexif d’origine recherche les en-têtes MZ et PE. Ces en-têtes sont devenus des points de détection. Pour surmonter ce problème, Cobalt Strike a ajouté
La documentation Cobalt Strike indique que
Une fois configurés,
Ces octets doivent être uniques, sans quoi le chargeur réflexif ne pourra pas les trouver. En outre, les octets de l’en-tête MZ doivent être de type non-opération et exécutables. Ils ne peuvent pas être des valeurs comme
Après avoir découvert ce point de détection potentiel, j’ai mis au point une autre méthode pour trouver l’adresse de base de la DLL beacon brute. Cette méthode utilise un chasseur d’œufs capable d’effectuer une recherche à rebours à partir de
L’adresse
Comme nous n’avons pas un accès facile au moteur Java Malleable PE, le
Script Aggressor pour écrire un œuf dans la DLL beacon brute et afficher les modifications dans la console de script Cobalt Strike.
Le code UDRL doit connaître la valeur de l’œuf écrite dans la DLL beacon brute par le script UDRL. Une fois l’œuf connu, le chasseur d’œufs en recherche deux instances, comme indiqué dans le code ci-dessous :
Code d’assemblage Intel x64 pour un chasseur d’œufs qui recherche à rebours deux instances d’un œuf 64 bits.
Maintenant que les en-têtes MZ et PE ne sont plus utilisés, nous pouvons les supprimer dans le script UDRL Aggressor :
Script Aggressor pour masquer les MZ, PE et les octets non utilisés de la bannière DOS située dans les en-têtes de la DLL beacon brute.
Il existe également un autre moyen, spécifique à Cobalt Strike, de découvrir l’adresse de base de la DLL beacon brute. Comme nous l’avons vu ci-dessus, les octets initiaux dans le stub d’appel du chargeur réflexif stockent l’adresse de base de la DLL beacon brute dans le
Pour examiner tout cela plus en détail dans le débogueur, nous générons un beacon, y ajoutons un point d’arrêt (
Capture d’écran X64dbg de l’exécution du stub du chargeur réflectif pour voir que l’adresse de base de la DLL Beacon brute est sauvegardée dans le registre RDI avant d’appeler le chargeur réflectif.
Voici un exemple pratique montrant comment obtenir l’adresse de base brute de la DLL Beacon à partir du stub du chargeur réflectif :
Code C d’assemblage en ligne pour obtenir l’adresse de base de la DLL beacon brute à partir du registre RDI.
Avec l'adresse de base de la DLL beacon brute, nous pouvons maintenant obtenir les valeurs dont nous avons besoin pour charger beacon dans l’espace d’adressage virtuel du processus.
Le tableau ci-dessous répertorie les valeurs dont nous avons besoin à partir des en-têtes de la DLL beacon brute, les emplacements où nous les trouverons et leur type :.
Tableau répertoriant les valeurs provenant de l’en-tête de la DLL beacon brute, utiles pour charger la DLL beacon.
Tous les contenus des en-têtes ne sont pas nécessaires au chargement de la DLL beacon. Les valeurs requises peuvent être reconditionnées ou brouillées. Les valeurs non obligatoires peuvent être supprimées ou randomisées.
Une fois que nous connaissonsSizeOfImagef
Différentes méthodes peuvent être utilisées pour allouer de la mémoire à la DLL beacon virtuelle. Le type de mémoire varie selon la méthode utilisée. Les différentes méthodes prises en charge par le chargeur réflexif par défaut de Cobalt Strike sont les suivantes :
Tableau présentant les options d’allocation de mémoire de Cobalt Strike pour la DLL beacon virtuelle.
On peut aller encore plus loin avec UDRL. On peut utiliser la version NTAPI de ces fonctions. En outre, les fonctions NTAPI peuvent être appelées via des appels système directs ou indirects, ce qui peut contribuer ou non à renforcer les capacités d’évitement.
Lorsque la méthode d’allocation est définie sur
Exemple de code du projet BokuLoader montrant qu’un appel système direct est utilisé pour allouer de la mémoire à la DLL beacon virtuelle.
L’image ci-dessous montre un exemple de code utilisant les méthodes HellsGate et HalosGate pour déterminer les numéros d’appel système :
Exemple de code du projet BokuLoader montrant comment les appels système sont détectés à partir du processus.
Maintenant que nous avons alloué de la mémoire à notre DLL beacon virtuelle, nous devons copier les sections du beacon à partir de leurs offsets de fichiers bruts, tels qu’ils existent dans la DLL beacon brute, vers la mémoire allouée au niveau de leurs offsets virtuels correspondants.
Si nous avons alloué notre mémoire avecREADWRITE la section et sa taille. Avant d’appeler le point d’entrée de la DLL Beacon virtuelle, nous devrons modifier les protections de mémoire de la section exécutable.
Allouer notre mémoire avec
Vous trouverez ci-dessous un exemple de code simplifié, tiré du projet BokuLoader, qui le démontre :
Exemple de code du projet BokuLoader montrant les sections copiées de la DLL beacon brute vers la DLL beacon virtuelle.
Voici quelques fonctionnalités d’évitement concernant les sections de chargement :
Dans le projet public BokuLoader, les en-têtes de la DLL beacon ne sont pas copiés de la DLL beacon brute vers la DLL beacon virtuelle. Actuellement, les premiers
Une autre possibilité d’évitement consiste à faire chiffrer les sections par le script UDRL Aggressor. Les sections peuvent être déchiffrées en mémoire par l’UDRL, à l’aide d’une clé partagée entre l’UDRL et le script Aggressor de l’UDRL.
Le beacon HTTP/S x64 repose sur quatre DLL pour fonctionner correctement. Si ces DLL ne sont pas encore chargées dans le processus, notre chargeur réflexif devra le faire.
Les quatre DLL sont répertoriées dans le répertoire d’importation de la DLL beacon HTTP/S :
Capture d’écran de PE-Bear listant les DLL du répertoire d’importation de la DLL beacon.
Le chargeur réflexif intégré à Cobalt Strike utilise l’API kernel32.LoadLibraryA pour le chargement des DLL.
Le chargement des DLL peut être réalisé de différentes manières, avec différentes considérations de sécurité opérationnelle. Voici quelques méthodes :
Si la DLL existe déjà dans le processus, alors les API Windows ci-dessus peuvent toujours être utilisées pour obtenir les adresses de base, bien que cela puisse déclencher des alertes de détection indésirables.
Sinon, le PEB contient également un pointeur vers
<a title="https://learn.microsoft.com/fr-fr/windows/win32/api/winternl/ns-winternl-peb_ldr_data" href="https://learn.microsoft.com/fr-fr/windows/win32/api/winternl/ns-winternl-peb_ldr_data">_PEB_LDR_DATA</a>
struct. Vous y trouverez une liste liée des DLL chargées au cours du processus et de leurs informations relatives (
). BokuLoader en tire parti pour découvrir les informations relatives aux DLL en évitant les appels d’API inutiles.
Si la DLL n’existe pas dans le
Le chargement réflexif imbriqué n’est pas facile à utiliser pour charger les dépendances DLL, car les chargeurs réflexifs n’enregistrent généralement pas la DLL dans le processus. Le code externe à la DLL ne peut pas utiliser correctement une DLL chargée de manière réflexive. Le projet DarkLoadLibrary est peut-être capable de charger correctement une DLL en mémoire sans déclencher un événement de chargement d’image du noyau.
Exemple de code du projet BokuLoader montrant comment les adresses de base des DLL chargées peuvent être résolues en parcourant InMemoryOrderModuleList.
Une fois les DLL nécessaires chargées dans le processus, les API listées dans le répertoire d’importation doivent être résolues. Les adresses API devront ensuite être écrites dans la table d’adresses d’importation (IAT) de la DLL beacon virtuelle. Ainsi, beacon sait à quelle adresse accéder lorsqu’il a besoin d’appeler des API comme
L’entrée d’importation devra être résolue soit par l’ordinal, soit par la chaîne de noms.
Sur l’image ci-dessous, on voit que la DLL beacon Cobalt Strike utilise une combinaison d’ordinaux et de chaînes de noms pour les entrées d’importation :
Capture d’écran de PE-Bear montrant certaines entrées d’importation pour la DLL beacon devant être résolues par l’ordinal.
Le chargeur réflexif Cobalt Strike intégré utilise
Voici quelques méthodes d’évitement pour résoudre les adresses d’API :
GetProcAddress
NTDLL.LdrGetProcedureAddress
BokuLoader utilise une implémentation de code personnalisée deGetProcAddress
La fonction est capable de traiter aussi bien les chaînes de noms que les ordinaux. Si l’adresse retournée pour l’entrée d’importation est une redirection vers une autre DLL, BokuLoader utilise par défaut pour résoudre le transfert.
Lors de l’écriture de l’IAT, le hooking peut être mis en œuvre en écrivant les adresses virtuelles des fonctions hook que nous avons implémentées, et non l’adresse virtuelle des API prévues. Tant que la sortie attendue est renvoyée à beacon lorsque l’adresse de l’IAT est appelée, nous pouvons exécuter du code supplémentaire avant de revenir à beacon. Les prochains articles et les versions publiques de BokuLoader montreront comment tirer parti du hooking IAT pour des fonctionnalités d’évitement avancées.
Avec une version récente, le projet public BokuLoader prend en charge la fonctionnalité PE malléable issue du profil Cobalt Strike C2 avec une implémentation personnalisée. En modifiant la clé de masquage dans laBokuLoader.cna
En ce qui concerne la sécurité opérationnelle, il est important de savoir que les moteurs de mise en correspondance des schémas sont capables de forcer les masques XOR à un seul octet. Dans les prochains articles, nous verrons comment créer notre propre moteur Malleable PE en utilisant les fonctionnalités de script de Cobalt Strikes Aggressor pour brouiller Beacon et ainsi contourner la mise en correspondance des schémas.
La DLL beacon comporte de nombreuses relocalisations qui doivent être résolues et écrites dans le tableau de relocalisation de base de la DLL beacon virtuelle avant son exécution.
Dans PE-Bear, nous voyons que la DLL beacon a par défaut l’adresse de base d’image de
Capture d’écran de PE-Bear montrant l’adresse de base d’image de la DLL beacon.
Avant de commencer à écrire des relocalisations, nous devons calculer le delta entre l’adresse de base de notre DLL beacon virtuelle et l’adresse de base codée en dur.
Par exemple, supposons que l’adresse de base de notre DLL beacon virtuelle soit
Ensuite, pour déterminer l’adresse virtuelle de chaque entrée de relocalisation dans le tableau de relocalisation de base, nous ajoutons le delta de l’adresse de base à l’adresse de l’entrée de relocalisation codée en dur pour déterminer la relocalisation dans notre DLL beacon virtuelle.
L’image ci-dessous montre que les entrées de relocalisation du beacon sont écrites à l’envers au format little endian :
Capture d’écran PE-Bear montrant plusieurs entrées de relocalisation au format little endian.
L’adresse codée en dur pour cette entrée de relocalisation est
Nous ajoutons cette adresse au delta de l’adresse de base pour obtenir l’adresse virtuelle de la relocalisation telle qu’elle figure dans la DLL beacon virtuelle :
Pour chaque entrée de relocalisation, nous devrons vérifier que le type est
<a title="https://learn.microsoft.com/fr-fr/windows/win32/debug/pe-format" href="https://learn.microsoft.com/fr-fr/windows/win32/debug/pe-format">IMAGE_REL_BASED_DIR64 (0xA)</a>
. Si cela est faux, nous n’écrirons pas la relocalisation.
Une fois que nous avons déterminé l’adresse virtuelle de relocalisation telle qu’elle figure dans la DLL beacon virtuelle, nous l’écrivons dans l’espace mémoire qui contient l’adresse de l’entrée de relocalisation codée en dur.
Si vous souhaitez en savoir plus sur les relocalisations PE, consultez le code de la fonction doRelocations dans le projet public BokuLoader. Avant de publier cet article de blog, j’ai remplacé le code de relocalisation d’assemblage par un code C lisible par l’humain, afin d’aider ceux qui souhaitent connaître les détails techniques de la procédure.
L’exécution de Beacon peut être décomposée en trois étapes :
Si la mémoire que nous avons allouée à notre DLL beacon virtuelle est
Si nous avons alloué notre mémoire beacon virtuelle comme étant non exécutable (
Dans le projet public BokuLoader, la modification des protections de mémoire est effectuée par des appels système directs à
Exemple de code du projet BokuLoader illustrant la modification de la section .text de la DLL beacon virtuelle à exécuter.
Les
Pour que la DLL beacon virtuelle s’exécute correctement, elle doit d’abord être initialisée en appelant le point d’entrée de la DLL beacon virtuelle. Le premier argument est l’adresse de base de la DLL beacon virtuelle. Le deuxième argument est
Exemple de code du projet BokuLoader initialisant la DLL beacon virtuelle.
Après avoir initialisé la DLL beacon virtuelle, nous pouvons soit renvoyer le point d’entrée du beacon virtuel au stub du chargeur réflexif d’appel, soit appeler le point d’entrée de la DLL beacon virtuelle dans notre UDRL avec
Contrairement à une DLL classique, dont le premier argument
<a href="https://learn.microsoft.com/fr-fr/windows/win32/dlls/dllmain">DLLMAIN</a>
serait l’adresse de base de la DLL virtuelle, beacon attend l’adresse de base de la DLL beacon brute. Si cette dernière n’est pas fournie, certaines fonctionnalités d’évitement de Malleable PE peuvent échouer.
Exemple de code du projet BokuLoader montrant deux façons différentes d’exécuter la DLL beacon virtuelle.
J’espère que cet article de blog aidera les équipes rouges et bleues à mieux comprendre Cobalt Strike et le processus de chargement réflectif. Il existe encore de nombreuses opportunités d’évasion qui peuvent être mises en œuvre grâce à un chargement réflectif. Avec une compréhension plus approfondie de ces concepts, les organisations peuvent mieux se préparer à une défense efficace contre les menaces cybernétiques.
Les prochains articles de cette série porteront sur l’intégration de l’UDRL avec les fonctions d’évitement actuelles de Cobalt Strike, sur les fonctions d’évitement non documentées déjà présentes dans le BokuLoader public, ainsi que sur les fonctions avancées qui n’ont pas encore été mises à la disposition du public. Ne manquez pas ces informations détaillées et techniques pour tirer pleinement profit de Cobalt Strike grâce au développement d’UDRL !