
modèle pour développer des canaux C2 personnalisés pour Cobalt Strike à l'aide de hooks IAT appliqués par un chargeur réflectif.
Ceci est un simple PoC et un modèle pour développer des canaux C2 personnalisés pour Cobalt Strike à l'aide de hooks IAT appliqués par un loader réflexif.
Article de blog : https://codex-7.gitbook.io/codexs-terminal-window/red-team/cobalt-strike/building-custom-c2-channels-by-hooking-wininet
Démo GIF du canal TCP

les fichiers customCallback.h, hook.c, hash.h et hook.h sont des modèles approximatifs, mais ils peuvent être déposés dans Crystal kit pour être utilisés tels quels comme PoC. Placez-les dans le dossier udrl/src, en remplaçant les copies existantes. tcg.h est inchangé (issu du tradecraft garden de Raphael Mudge)
Les exemples dans le dossier examples/ peuvent être utilisés tels quels, mais ce sont des exemples et les considérations opérationnelles n'ont pas été prises en compte lors de leur écriture. À utiliser avec précaution.
Les exemples actuels sont :
Notez que les capacités d'évasion d'origine de Crystal kit, telles que l'implémentation de Draugr et le sleep masking, ont été retirées de hook.c afin de garder cette base de code propre et portable. Si vous souhaitez conserver ces fonctionnalités, vous pouvez les rajouter à partir des copies d'origine dans Crystal kit.
Vous devez sélectionner la bibliothèque http appropriée, winhttp/wininet, comme bibliothèque http à utiliser lors de la génération de la DLL du beacon.
Le profil malleable avec lequel cela a été testé est fourni dans le dépôt — en théorie, (la plupart des) autres profils malleable devraient fonctionner, mais ce modèle a été testé avec ce profil (tiré de GraphStrike).
L'interface ExternalC2 officielle est pénible à utiliser — elle nécessite le staging d'un beacon SMB PAR-DESSUS le canal externalc2, et (jusqu'à la version 4.10) ne prenait pas en charge la communication avec un beacon SMB préfabriqué sans staging préalable via l'agent externalc2. Même dans son état actuel, elle reste liée à l'architecture suivante :
smb beacon --named pipe--> externalc2 agent --custom channel--> externalc2 handler --> teamserver
Oui, je sais que UDC2 a été ajouté dans Cobalt Strike 4.12. Ce n'est de toute façon qu'une expérience amusante pour répliquer cette capacité dans l'UDRL.
Le concept est simple — si vous pouvez hooker les WinAPI utilisées pour le callback, vous disposez déjà de toutes les données nécessaires pour un callback. Cette méthode d'implémentation de canaux C2 personnalisés a déjà été utilisée auparavant, comme on le voit dans GraphStrike.
Cependant, l'implémentation de GraphStrike reste encore assez liée au protocole HTTP lui-même. L'objectif de ce dépôt est de fournir un modèle facile à utiliser, à modifier et à étendre pour implémenter n'importe quel canal personnalisé, HTTP ou autre, avec peu de modifications du code environnant.
Ce modèle n'est évidemment pas destiné à être utilisé dans son état par défaut. Pour implémenter le canal C2 de votre choix, vous devez modifier ce qui suit :
customCallback fonction pour effectuer les choses suivantes :
Vous pouvez utiliser l'hôte et le port d'origine de la requête http (que vous définissez depuis cobalt) via les arguments :
const char *host
INTERNET_PORT port
et modifier la fonction handleCallback() dans broker.py pour qu'elle fasse ce qui suit :
C'est tout.
À titre d'exemple, le PoC dans la fonction customCallback() de customCallback.h fait actuellement ce qui suit :
Le PoC dans la fonction handleCallback de broker.py fait actuellement ce qui suit :
process_encoded_request(encoded_request)usage: broker.py [-h] --host HOST --port PORT
où l'hôte et le port pointent vers le listener http de votre teamserver. Le broker extrait la requête http et l'envoie au listener réel.
C'est probablement la façon la plus « paresseuse » de le faire, mais aussi l'une des plus stables. En gros, cela encapsule l'intégralité de la requête HTTP et l'achemine vers le broker comme vous le souhaitez, qui l'envoie ensuite pour de vrai au teamserver, pour s'exécuter en tant que beacon HTTP, de façon totalement transparente pour le teamserver.
J'ai essayé une implémentation (techniquement) plus propre qui consistait à utiliser un profil Malleable C2 minimaliste (j'ai utilisé celui de GraphStrike, en fait) et à analyser les différents composants des données du beacon conformément à la spécification malleable dans les hooks wininet eux-mêmes (id, metadata, output, etc.). Cela aurait permis de réduire légèrement la taille des blobs de callback, mais cette implémentation a été abandonnée car la base de code devenait inutilement compliquée à cause de l'analyse de la requête, et elle exigeait également l'utilisation de ce profil spécifique (ce n'est pas si grave, mais c'est un peu bricolé).
J'ai estimé que cette implémentation était meilleure car elle dépend moins du profil malleable bricolé (techniquement, elle exige toujours que la sortie de la réponse du beacon soit envoyée dans le corps de la réponse, mais c'est la seule exigence) et le code était 10 fois plus facile à lire.
Il convient également de noter qu'il n'y a ni obfuscation ni chiffrement sur le json de la requête http, en dehors de l'encodage base64 — vous êtes libre d'ajouter le vôtre, mais comme les callbacks de Beacon sont déjà chiffrés, techniquement aucune donnée ne risque d'être déchiffrée. Le pire qui pourrait arriver, c'est que le blob base64, s'il est récupéré, puisse être identifié comme une requête HTTP. Faites de cette information ce que vous voulez.
Oui, j'ai utilisé un LLM pour écrire une partie du code et des commentaires ; personne n'aime écrire de la documentation ou du parsing json passe-partout en C à partir de zéro. Poursuivez-moi en justice.
Avertissement habituel, je ne suis pas responsable des crimes contre l'humanité que vous pourriez commettre ni de la guerre nucléaire que vous pourriez provoquer en utilisant ce code mal écrit.