Introduction à Cobalt Strike
Cobalt Strike se positionne comme une plateforme de C2 (Command and Control) de pointe, plébiscitée dans le cadre des opérations d'équipes rouges pour sa robustesse en matière de post-exploitation.
Composants Clés
Agent Beacon
Le Beacon est l'agent de post-exploitation de Cobalt Strike. Sa fonction principale est d'établir et de maintenir la communication avec le serveur d'équipe, de récupérer des ordres d'exécution, et de renvoyer les résultats. Bien qu'initialement implémenté comme une DLL Windows, le Beacon peut être conditionné sous diverses formes, telles que des exécutables, des scripts PowerShell, ou du shellcode indépendant de la position.
Serveur d'Équipe (Team Server)
Le Serveur d'Équipe agit comme le pivot central de la gestion des opérations. Il héberge le système de contrôle, la journalisation des événements, et orchestre certaines interactions avec les agents Beacon.
Client Opérateur
Le Client est l'interface graphique utilisée par les membres de l'équipe rouge. Il permet de se connecter à un ou plusieurs serveurs d'équipe pour interagir avec les charges utiles Beacon déployées.
Modèle d'Opérations Distribuées
Cobalt Strike favorise un modèle d'opérations distribuées. Cette approche préconise l'utilisation de serveurs d'équipe dédiés pour chaque phase d'une campagne (par exemple, accès initial, post-exploitation, persistance). L'objectif est d'assurer la résilience : si une partie de l'infrastructure est compromise ou détectée, les autres canaux restent opérationnels, permettant de maintenir l'accès. Grâce à ses fonctionnalités d'intégration, Cobalt Strike facilite grandement cette architecture distribuée.
Les Listeners (Écouteurs)
Fonctionnalité d'un Listener
La première étape de la configuration de Cobalt Strike consiste à créer un ou plusieurs listeners. Un listener est un profil qui spécifie le protocole et les paramètres que les charges utiles Beacon utiliseront pour communiquer avec le serveur d'équipe. Cobalt Strike prend en charge des protocoles variés par défaut, incluant DNS, HTTP, HTTPS, SMB et TCP. Ces options permettent de masquer l'activité réseau des Beacons en la faisant ressembler à du trafic réseau d'entreprise légitime.
Typologies de Listeners
Egress (Sortie Directe)
Les Beacons de type Egress communiquent directement avec le serveur d'équipe en traversant les frontières réseau de l'environnement cible. Les listeners DNS et HTTP/S sont des exemples de listeners Egress.
Peer-to-Peer (P2P)
Les Beacons P2P ne se connectent pas directement au serveur d'équipe. Ils acheminent leur trafic via un autre Beacon. Plusieurs Beacons P2P peuvent être chaînés, mais ils doivent ultimement se connecter à un Beacon Egress pour que le trafic atteigne le serveur d'équipe. Les protocoles SMB et TCP sont utilisés pour les listeners P2P.
Listener HTTP
Un listener HTTP configure le Beacon pour qu'il communique avec le serveur d'équipe via des requêtes HTTP GET et/ou POST. Par défaut, il utilise les requêtes GET pour récupérer les tâches et les requêtes POST pour envoyer les résultats. Le serveur d'équipe intègre un serveur web pour gérer ces interactions.
Hôtes HTTP
Ce sont les cibles des requêtes HTTP du Beacon, spécifiables par adresse IP ou nom de domaine. Il est possible d'utiliser une adresse IP ou un domaine qui résout directement vers le serveur d'équipe, ou d'employer un redirecteur. Un redirecteur est un hôte intermédiaire qui agit comme un proxy entre le Beacon et le serveur d'équipe. Des outils comme iptables, socat, Apache et NGINX sont couramment utilisés pour cette fonction.
Stratégies de Rotation des Hôtes
Lorsque plusieurs hôtes HTTP sont configurés, une stratégie de rotation dicte la manière dont le Beacon les utilise. Cela aide à contrer les techniques d'analyse de fréquence du trafic C2 et offre une résilience si des hôtes sont bloqués. Les options disponibles sont :
- Rond-Point (Round-Robin) : Le Beacon parcourt séquentiellement la liste des hôtes, utilisant chacun pour une seule requête avant de passer au suivant.
- Aléatoire (Random) : Le Beacon sélectionne un hôte différent au hasard pour chaque requête.
- Basculement (Failover) : Disponible sous les formes
failover-xetfailover-m/h/j. Le Beacon persiste avec un hôte jusqu'à ce qu'une condition de basculement soit remplie (par exemple,xéchecs consécutifs, ou échecs sur une période donnée en minutes/heures/jours). Il passe ensuite à l'hôte suivant. - Rotation (Rotate) : Similaire au rond-point, mais chaque hôte est utilisé pendant une période spécifiée (
rotate-m/h/j) avent que le Beacon ne bascule vers le suivant.
Stratégie de Réessai Maximal
Cette configuration définit la politique d'auto-destruction du Beacon en cas de perte complète de communication avec tous les hôtes HTTP.
- Aucun (None) : Le Beacon continue de fonctionner indéfiniment, sauf s'il est arrêté par d'autres moyens.
- Sortie (Exit) : Suivant le format
exit-[max_tentatives]-[inc_tentatives]-[durée][m/h/j]. Par exemple,exit-50-25-1hindique au Beacon d'augmenter son temps de sommeil à 1 heure après 25 échecs consécutifs, et de s'arrêter après 50 échecs consécutifs.
Hôte HTTP (Stager)
Cet hôte est exclusivement utilisé par le payload stager. Même si plusieurs hôtes HTTP sont configurés, le stager n'en utilise qu'un seul pour récupérer la phase complète du payload. Cet hôte peut être identique ou différent des hôtes HTTP principaux, tant qu'il résout vers le serveur d'équipe.
Profil
Les profils C2 personnalisables permettent de définir des variations de trafic. Ce menu déroulant permet de sélectionner un profil spécifique. D'autres options n'apparaîtront que si des variantes ont été configurées dans le profil chargé.
Port HTTP (C2)
Le port HTTP par lequel le Beacon tente de se connecter au serveur d'équipe. Pour les configurations standards, il doit correspondre au protocole (ex: 80 ou 8080 pour HTTP).
Port HTTP (Bind)
Le port sur lequel le serveur d'équipe va lier son serveur web intégré. Si non spécifié, il utilisera le même port que le port C2. Un port de liaison différent est utile pour la redirection de port, par exemple, pour faire écouter un redirecteur sur le port C2 mais rediriger vers un port différent sur le serveur d'équipe. Ceci permet de faire fonctionner plusieurs listeners HTTP sur le même serveur d'équipe tout en faisant communiquer les Beacons via le port 80.
En-tête d'Hôte HTTP (Host Header)
La configuration d'un en-tête d'hôte ici est propagée au payload Beacon, évitant ainsi d'avoir à l'encoder directement dans le profil C2. Cela facilite l'usurpation de domaine.
Proxy HTTP
Le Beacon tente d'utiliser les paramètres de proxy Internet du système sur lequel il s'exécute par défaut. Cependant, il est possible de spécifier un proxy HTTP ou SOCKS directement dans la configuration du Beacon. Cette option ne s'applique pas aux stagers.
Garde-fous (Guardrails)
Les garde-fous empêchent l'exécution de payloads Beacon non-étagés si des conditions spécifiques ne sont pas remplies. Cela est utile si un payload est transféré en dehors de l'environnement cible (par exemple, un e-mail de phishing est transféré à un tiers, ou une équipe bleue tente d'analyser le payload dans un sandbox). Les critères peuvent inclure l'adresse IP de l'hôte, le nom d'utilisateur du compte, le nom d'hôte ou le domaine de l'hôte.
Listener DNS
Le listener DNS instruit le Beacon de communiquer avec le serveur d'équipe via des requêtes DNS (recherches d'enregistrements A, AAAA ou TXT). Le serveur d'équipe lance un serveur DNS intégré pour traiter ces requêtes.
Pour qu'un Beacon DNS fonctionne, il est impératif d'ajouter les enregistrements DNS nécessaires pour que votre serveur d'équipe soit autoritaire pour un ou plusieurs sous-domaines spécifiques. Habituellement, votre registraire de domaine est autoritaire pour votre domaine de premier niveau (TLD).
Un Beacon DNS s'enregistre auprès du serveur d'équipe en effectuant une recherche d'enregistrement A pour le domaine défini dans le listener. Contrairement à d'autres Beacons (comme HTTP), le Beacon DNS ne transmet pas immédiatement toutes ses métadonnées. Cela se traduit par une ligne "vide" initialement pour un nouveau Beacon DNS. Les métadonnées ne sont envoyées qu'une fois qu'une tâche est attribuée au Beacon. La commande checkin existe spécifiquement pour forcer le transfert des métadonnées du Beacon.
Résolveur DNS
Par défaut, un Beacon DNS utilise le résolveur DNS configuré sur la machine hôte. Il est possible de remplacer cette configuration en spécifiant l'adresse IP ou le nom d'hôte d'un résolveur DNS personnalisé dans ce champ.
Listeners SMB
Le listener SMB est un exemple de listener qui ne se lie pas à une machine virtuelle de serveur d'équipe ; il sert uniquement de modèle pour la génération de payload.
Lors de son exécution, un payload Beacon SMB crée un tube nommé SMB (SMB Named Pipe) en utilisant le nom spécifié dans la configuration du listener. Il nécessite ensuite qu'un autre Beacon se connecte à ce tube nommé et relaye le trafic entre le Beacon SMB et le serveur d'équipe. Ceci est réalisé via le protocole SMB de Windows sur le port 445.
Cobalt Strike utilise par défaut des noms de tubes comme msagent_##, où ## est une valeur hexadécimale aléatoire. Cependant, vous pouvez choisir n'importe quel nom de tube, à condition qu'il n'entre pas en conflit avec un nom existant sur l'hôte. Plusieurs listeners SMB avec des noms de tubes différents peuvent être créés.
Listeners TCP
À l'instar du listener SMB, le listener TCP n'indique pas au serveur d'équipe d'écouter un port TCP. Il fournit plutôt les informations de configuration lors de la génération d'un payload Beacon TCP.
Lors de l'exécution, un payload Beacon TCP se lie et écoute sur le port C2 spécifié dans la configuration du listener. Il nécessite ensuite qu'un autre Beacon se connecte à ce port et relaie le trafic entre le Beacon TCP et le serveur d'équipe. Si l'option "Bind only to localhost" n'est pas cochée, le Beacon se liera à 0.0.0.0. Si elle est cochée, il se liera à 127.0.0.1.
Un listener TCP lié à 0.0.0.0 peut être utilisé pour le mouvement latéral, tandis qu'un listener lié à 127.0.0.1 est utile pour l'élévation de privilèges. Il est possible de créer plusieurs listeners TCP avec des numéros de port et des configurations de liaison différents.
Payloads Beacon
Le Beacon est intrinsèquement une DLL Windows. Comme d'autres modules (DLL) chargés dans un processus Windows (tels que ntdll.dll ou kernel32.dll), il fournit des fonctionnalités nécessaires. Pour exécuter un payload Beacon, sa DLL doit être chargée en mémoire dans un processus, et généralement un nouveau thread doit être démarré pour appeler son point d'entrée.
Le concept de "sans fichier sur le disque" est crucial ici : les DLL Windows sont typiquement chargées depuis le disque (par exemple, depuis %windir%\system32), mais cette approche est sous-optimale pour des opérations discrètes. Le Beacon est donc conçu comme une DLL réfléchissante, basée sur les travaux de Stephen Fewer, ce qui lui permet d'être chargé directement depuis la mémoire, sans nécessiter de dépôt sur le disque. Une DLL réfléchissante contient un chargeur qui se re-mappe en mémoire et gère le chargement des dépendances. La documentation du projet de Stephen Fewer sur GitHub fournit des détails sur ce processus.
Voici une représentation simplifiée de la structure PE :
La DLL du Beacon exporte une fonction nommée ReflectiveLoader. Lorsqu'elle est appelée, cette fonction parcourt sa propre image, mappe une nouvelle copie d'elle-même en mémoire, puis appelle son point d'entrée. Étant exportée sous forme de shellcode, un petit stub de shellcode est écrit dans l'en-tête DOS pour rediriger l'exécution vers la fonction ReflectiveLoader.
Lorsqu'un payload est généré par Cobalt Strike, les informations du listener sont intégrées dans la DLL Beacon réfléchissante. Cette DLL est ensuite transformée en code indépendant de la position (PIC), c'est-à-dire du "shellcode". Ce shellcode est ensuite intégré dans les divers modèles de payload (ou "artefacts") que Cobalt Strike peut générer, comme les fichiers .exe, .dll, et .ps1. Le payload lui-même utilise des techniques d'injection de processus pour charger et exécuter ce shellcode. Un pseudo-code pourrait ressembler à ceci :
// Représentation du shellcode du Beacon (code machine auto-suffisant)
unsigned char shellcodeDuBeacon[1024] = "<votre_shellcode_ici>";
// Allouer un segment de mémoire avec des droits d'exécution, lecture et écriture
void* zoneExec = VirtualAlloc(
NULL, // Adresse de base préférée (NULL = laissée au système)
sizeof(shellcodeDuBeacon),// Taille à allouer
MEM_COMMIT | MEM_RESERVE, // Type d'allocation
PAGE_EXECUTE_READWRITE // Protection de la mémoire
);
// Copier le shellcode dans la zone mémoire allouée
if (zoneExec != NULL) {
memcpy(zoneExec, shellcodeDuBeacon, sizeof(shellcodeDuBeacon));
// Lancer un nouveau thread pour exécuter le shellcode copié
CreateThread(
NULL, // Attributs de sécurité
0, // Taille de la pile
(LPTHREAD_START_ROUTINE)zoneExec, // Fonction de démarrage (le shellcode)
NULL, // Argument à passer à la fonction
0, // Flags de création
NULL // ID du thread
);
}
// else { gérer l'échec de l'allocation }
</votre_shellcode_ici>
Le "Nouveau" Chargeur Préfixé
Depuis Cobalt Strike 4.11, le Beacon utilise un nouveau chargeur basé sur celui employé par DoublePulsar. Ce chargeur, développé par la NSA et divulgué par "The Shadow Brokers", est tristement célèbre pour ses liens avec EternalBlue et le ransomware WannaCry.
Il s'agit d'un chargeur "préfixé", ce qui signifie qu'il est situé en amont du PE (Portable Executable) et n'en fait pas partie intégrante. Cette conception offre une plus grande discrétion et flexibilité par rapport aux chargeurs traditionnels. Il n'exige pas l'écriture d'un stub shellcode dans l'en-tête DOS du PE, peut charger de manière réfléchissante n'importe quel PE, et n'impose pas que le PE exporte des fonctions spécifiques pour le chargement. De plus, il offre davantage d'options pour l'obfuscation du PE, qui peut être encodé, chiffré ou compressé, tant que le chargeur est capable de défaire ces transformations.
Payloads Étagés (Staged) vs Non-Étagés (Stageless)
Les frameworks d'attaque, tels que Metasploit et Cobalt Strike, séparent souvent l'"exploit" du "payload". Par exemple, dans une vulnérabilité d'exécution de code due à un débordement de tampon, l'exploit est le code qui déclenche le débordement, tandis que le payload est le code que vous souhaitez exécuter (comme un Meterpreter ou un shellcode Beacon). La taille du payload transmissible est souvent limitée par la nature de la vulnérabilité ou le canal d'accès.
Pour contourner ces limitations, la technique de "payload staging" a été développée. Elle implique un petit programme (le stager) qui, une fois exécuté, récupère le payload complet (la "stage") via un canal différent (par exemple, HTTP), l'injecte dans une nouvelle région mémoire et lui passe le contrôle d'exécution. Bien que le staging soit efficace pour les contraintes de taille, il présente des inconvénients.
Sécurité des Payloads
Au démarrage, le serveur d'équipe Cobalt Strike génère une nouvelle paire de clés publique/privée unique. Chaque payload non-étagé généré par ce serveur intègre sa clé publique. Le Beacon utilise cette clé pour chiffrer ses métadonnées lors de la communication, assurant qu'il ne peut communiquer qu'avec le serveur d'équipe qui l'a généré. De plus, chaque Beacon utilise une clé de session unique pour chiffrer/déchiffrer les tâches et les données d'output échangées avec le serveur d'équipe. Cette clé de session est elle-même transférée au sein des métadonnées chiffrées, garantissant que seul le serveur d'équipe correct peut chiffrer et déchiffrer le trafic C2 du Beacon. Cela empêche les attaquants de facilement intercepter et prandre le contrôle des payloads non-étagés. Cependant, en raison de leur petite taille, les stagers n'offrent pas le même niveau de sécurité et sont vulnérables à la compromission lors de leur première exécution.
Sécurité Opérationnelle (OPSEC)
Pour minimiser la taille des stagers, certains protocoles que nous considérons aujourd'hui comme des "bonnes pratiques" sont omis. Par exemple, les payloads non-étagés évitent l'utilisation de mémoire RWX (lecture, écriture, exécution), car cela est inhabituel dans la plupart des processus Windows. Au lieu de cela, ils allouent d'abord de la mémoire RW, puis la convertissent en RX avant l'exécution. Les stagers, pour leur part, n'effectuent pas cette étape, car elle nécessiterait des appels d'API supplémentaires et donc plus de code. Les stagers sont souvent écrits en langage assembleur optimisé à la main, où chaque octet compte.
À titre indicatif, un stager Beacon pèse environ 890 octets, tandis qu'un Beacon non-étagé complet est d'environ 307 200 octets. Cela représente un facteur d'environ 345.
Génération de Payloads
Les payloads Beacon peuvent être générés via le menu "Payloads".
Application HTML (HTA)
Cette option génère un payload au format .hta, une combinaison de HTML et VBScript. Le VBScript étant supporté par Internet Explorer, ce format est couramment utilisé dans les attaques de phishing. Une boîte de dialogue vous permet de choisir le listener et la méthode d'exécution :
- Exécutable : Dépose un exécutable sur le disque et le lance.
- PowerShell : Utilise
powershell.exepour exécuter le stager en mémoire. - VBA : Utilise une macro VBA pour exécuter le stager en mémoire, en instanciant
Excel.Application. Nécessite Microsoft Office sur le système cible.
Ce HTA délivre toujours un payload Beacon x86.
Macro MS Office
Génère une macro VBA à insérer dans un document Office, un autre format de payload de phishing populaire.
Cette macro délivre toujours un payload Beacon x86.
Générateur de Payload Stager
Ces générateurs produisent des fichiers sources dans diverses langues, similaires à l'outil msfvenom de Metasploit. Ils sont utiles pour obtenir un tableau d'octets à importer dans votre propre "shellcode runner".
Par exemple, un stager en C produit :
/* longueur : 888 octets */
unsigned char buf[] = "\xfc\x48\x83\xe4[...tronqué...]\x12\x1d\x9b\x09";
Générateur de Payload Non-Étagé (Stageless)
Similaire au générateur de payload étagé, mais avec des options supplémentaires :
- Fonction de Sortie (Exit Function) : Contrôle l'API appelée par le Beacon lors d'une commande
exit.ProcessappelleExitProcess(terminant le processus hôte entier), tandis queThreadappelleExitThread(terminant seulement le thread du Beacon).ExitProcess(xprocess) etExitThread(xthread) ont des usages distincts. Utilisezxthreadsi le Beacon est injecté dans un processus existant, etxprocesssi le Beacon est injecté dans un processus que vous avez lancé. - Appel Système (System Call) : Indique au Beacon d'utiliser des appels système plutôt que des API Win32 standard pour ses opérations internes. Deux options :
- Direct : Utilise directement les versions
Nt*des fonctions. - Indirect : Effectue un saut indirect vers l'instruction correspondante dans la version
Nt*de la fonction.
- Direct : Utilise directement les versions
Payloads Windows Étagés/Non-Étagés
Ces options sont semblables aux générateurs de payloads étagés/non-étagés, mais produisent des exécutables pré-compilés au lieu de fichiers source bruts. Les formats de sortie incluent :
- Windows EXE : Un exécutable Windows standard.
- Windows Service EXE : Un exécutable Windows conçu pour interagir avec le gestionnaire de contrôle des services.
- Windows DLL : Une DLL avec plusieurs exports qui peuvent être appelés par des utilitaires comme
rundll32etregsvr32.
Ces payloads peuvent également être signés avec un certificat de signature de code.
Générer Tous les Payloads Non-Étagés Windows
Cette option génère toutes les variantes de payloads non-étagés possibles pour chaque listener disponible, pour les architectures x86 et x64.
Interaction avec le Beacon
Lorsqu'une nouvelle session Beacon s'enregistre pour la première fois auprès du serveur d'équipe, une entrée apparaît dans le journal des événements (View > Event Log), et une nouvelle session est affichée dans la vue tabulaire ou graphique, selon la sélection.
Les colonnes de la vue tabulaire sont intuitives :
- external : L'adresse IP externe de la cible, résolue par le serveur web de Cobalt Strike.
- internal : L'adresse IP interne de la machine.
- listener : Le nom du listener Egress utilisé par le Beacon.
- user : Le nom d'utilisateur sous lequel le processus Beacon s'exécute.
- computer : Le nom d'hôte de l'ordinateur.
- note : Un espace pour ajouter des notes personnalisées.
- process : Le nom du processus hôte du Beacon.
- pid : L'ID du processus hôte du Beacon.
- arch : L'architecture du Beacon (x64 ou x86).
- last : Le temps écoulé depuis la dernière communication du Beacon avec le serveur d'équipe (ms, s, min, h, j).
- sleep : Le temps de sommeil configuré pour le Beacon.
Une icône de moniteur bleue indique que le Beacon s'exécute avec un niveau d'intégrité moyen (privilèges d'utilisateur standard). Un moniteur rouge, avec un astérisque à côté du nom d'utilisateur, signale une exécution avec un niveau d'intégrité élevé (administrateur local ou privilèges SYSTEM).
Le Beacon s'identifie auprès du serveur d'équipe via des métadonnées envoyées par le protocole C2. Pour HTTP/S, ces métadonnées sont généralement intégrées dans l'URL, les en-têtes de requête ou les cookies d'une requête GET ou POST. Les métadonnées elles-mêmes sont chiffrées avec la clé publique du serveur d'équipe ayant généré le payload, ce qui empêche le Beacon de communiquer ou de recevoir des tâches d'autres serveurs d'équipe.
Pour "interagir" avec un Beacon, double-cliquez sur sa ligne ou faites un clic droit et choisissez "Interact". Cela ouvrira un nouvel onglet.
Pour voir la liste des commandes, tapez help dans la zone de texte inférieure et appuyez sur Entrée. Une brève description de chaque commande sera affichée.
Pour obtenir de l'aide sur une commande spécifique, utilisez help <commande>.
Pour envoyer une tâche au Beacon, tapez simplement une commande valide et appuyez sur Entrée. La tâche est alors ajoutée à une file d'attente. Vous pouvez mettre plusieurs commandes en file d'attente pendant que le Beacon attend de s'enregistrer. La file d'attente peut être effacée avec la commande clear. Lorsque le Beacon s'enregistre et que des tâches sont disponibles, le serveur d'équipe les fournit dans la réponse du trafic. Le client affiche également un bref journal indiquant que les tâches ont été envoyées.
Le Beacon traite chaque tâche dans l'ordre de la file d'attente et renvoie les résultats au client, qui les affiche à l'opérateur.
Vue Graphique des Sessions
Alors qu'un affichage tabulaire peut montrer un Beacon Egress et deux Beacons P2P, la relation exacte entre eux peut ne pas être immédiatement évidente. La vue graphique résout ce problème en visualisant clairement les interconnexions entre les Beacons.
Bien qu'elle affiche moins d'informations que la vue tabulaire, elle rend la relation entre chaque Beacon instantanément compréhensible.
L'icône de pare-feu et la ligne pointillée représentent un Beacon Egress qui communique directement avec le serveur d'équipe. Les lignes pleines représentent des connexions P2P. Les couleurs indiquent le protocole utilisé :
- Vert pointillé pour HTTP/S.
- Jaune plein pour SMB.
- Jaune pointillé pour DNS.
- Vert plein pour TCP.
Un clic droit n'importe où sur le fond noir affiche un menu avec des options de mise en page supplémentaires. La mise en page par défaut est "Tree Left View". Vous pouvez également faire un clic droit sur un Beacon pour accéder au même menu d'interaction que dans la vue tabulaire.