
Agent Adaptix C2 utilisant le linker PIC Crystal Palace et le système de modules PICO
Un agent C2 Adaptix construit avec Crystal Palace — un linker PIC (Position Independent Code) personnalisé et un système de modules PICO. Démontre comment créer des agents shellcode modulaires où chaque composant (transport, tâches, obfuscation) est un blob PICO séparé chargé au runtime.
Cet agent n'inclut aucune technique d'évasion et n'est pas destiné à être utilisé tel quel lors d'engagements. Il s'agit d'une implémentation de référence pour construire des agents avec Crystal Palace et le système de modules PICO.
Crystal Palace est un linker PIC qui prend des objets COFF compilés et produit des exécutables indépendants de la position. Concepts clés :
make pic +gofirst) — Le shellcode exécutable principal. Contient le code d'amorçage, le résolveur DFR et les marqueurs de sections où les modules PICO sont liés. Appelé directement par le loader.make object) — Blobs de code autonomes avec leurs propres sections code et données. Chargés au runtime via PicoLoad() depuis libtcg. Chaque PICO possède un point d'entrée (go()) appelable via un pointeur de fonction.MODULE$Function (ex. KERNEL32$VirtualAlloc) par des appels à resolve(mod_hash, func_hash) utilisant le hachage ROR13. Aucune table d'imports. Tous les arguments chaîne (noms de DLL, noms de fonctions) sont construits sur la pile sous forme de tableaux de caractères pour éviter le texte clair dans le binaire.entry_module, transport_module, etc.) via la directive link dans le fichier .spec.{LoadLibraryA, GetProcAddress} passée à PicoLoad() afin que les modules PICO puissent résoudre leurs propres symboles DFR.Source C → mingw-gcc → objets COFF → lien Crystal Palace → shellcode PIC brut → loader (Exe/Dll/Svc)
Le fichier .spec définit comment Crystal Palace lie l'ensemble :
x64:
load "Bin/obj/main.x64.o" # Core PIC
make pic +gofirst
foreach %LIBS: mergelib %_ # Fusionne libtcg
load "Bin/obj/entry.x64.o" # Entry PICO
make object
load "Bin/obj/crypto.x64.o" # fusionne crypto dans entry
merge
load "Bin/obj/packer.x64.o" # fusionne packer dans entry
merge
export
link "entry_module" # lie au marqueur de section
load "Bin/obj/transport.x64.o" # Transport PICO
make object
mergelib "lib/LibWinHttp/..."
export
link "transport_module"
... # task_module, obfuscation_module
dfr "resolve" "ror13" # résout tous les symboles DFR
export
Core PIC (main.c)
│
├── resolve() Pont DFR → recherche de hachage libtcg
├── AllocateAndLoadModule() PicoLoad de chaque PICO dans une région RWX partagée
│
└── appelle go() du module entry avec des pointeurs vers tous les autres modules
│
├── Module Entry (entry.c)
│ MvState, checkin, transact (format filaire RC4), boucle de tâches
│
├── Module Transport (transport.c)
│ POST HTTP/HTTPS via LibWinHttp
│
├── Module Tâches (tasks.c)
│ Dispatch des commandes : whoami (0x30), sleep (0x20), exit (0x10)
│
└── Module Obfuscation (obfuscation.c)
Sleep Ekko — chaîne ROP sur file de temporisation qui chiffre la mémoire
du module avec RC4 (SystemFunction033) pendant le sommeil, déchiffre au réveil
┌─────────────────────────────┐
│ Région RWX partagée │ VirtualAlloc(PAGE_EXECUTE_READWRITE)
│ ├── Code Entry │ PicoLoad → code ici
│ ├── Code Transport │
│ ├── Code Tâches │
│ └── Code Obfuscation │
├─────────────────────────────┤
│ Données Entry (RW) │ PicoLoad → données ici (allocation séparée)
│ Données Transport (RW) │
│ Données Tâches (RW) │
│ Données Obfuscation (RW) │
├─────────────────────────────┤
│ Core PIC (libéré après boot)│ Shellcode d'origine, libéré par le module entry
└─────────────────────────────┘
La région RWX partagée est ce qu'Ekko chiffre/déchiffre pendant les cycles de sommeil.
Tout le trafic est chiffré avec RC4 (chiffrement de flux, clé de 16 octets).
Envoi : [36B agent_id][RC4(payload)][16B clé (uniquement au premier checkin)]
Réception : [36B agent_id][RC4(réponse)]
src_beacon/Source/
├── main.c Core PIC — résolveur DFR, chargement de modules, amorçage
├── entry.c Entry PICO — état de l'agent, checkin, boucle de tâches, transact
├── transport.c Transport PICO — POST HTTP via LibWinHttp
├── tasks.c Task PICO — dispatch whoami/sleep/exit
├── obfuscation.c Obfuscation PICO — sleep Ekko (ROP sur file de temporisation + RC4)
├── crypto.c Chiffrement de flux RC4 (fusionné dans le PICO entry)
├── packer.c Packer binaire BE / parser LE (fusionné dans le PICO entry)
└── includes/
├── config.h Définitions de compilation (UUID, sleep, hôte/port/uri/ssl de callback)
├── crypto.h API RC4
├── packer.h API PackBuf / Parser
├── tcg.h libtcg de Crystal Palace (PicoLoad, findModuleByHash, etc.)
└── HTTP.h API LibWinHttp
| Commande | ID | Description |
|---|
x86_64-w64-mingw32-gcc (compilateur croisé MinGW)./setup.sh --ax ../AdaptixC2
whoami, sleep, exit depuis la console Adaptix
whoami | 0x30 | Retourne COMPUTER\username |
sleep <secondes> | 0x20 | Met à jour l'intervalle de callback |
exit thread|process | 0x10 | Termine l'agent |