
Adaptix C2-Agent mit Crystal Palace PIC-Linker und PICO-Modulsystem
Ein Adaptix C2-Agent, erstellt mit Crystal Palace – einem benutzerdefinierten PIC (Position Independent Code) Linker und PICO-Modulsystem. Demonstriert, wie modulare Shellcode-Agenten gebaut werden, bei denen jede Komponente (Transport, Aufgaben, Verschleierung) ein separates PICO-Blob ist, das zur Laufzeit geladen wird.
Dieser Agent enthält keine Umgehungstechniken und ist nicht dafür gedacht, unverändert in Einsätzen verwendet zu werden. Es handelt sich um eine Referenzimplementierung für den Bau von Agenten mit Crystal Palace und dem PICO-Modulsystem.
Crystal Palace ist ein PIC-Linker, der kompilierte COFF-Objekte entgegennimmt und positionsunabhängige ausführbare Dateien erzeugt. Kernkonzepte:
make pic +gofirst) – Der ausführbare Haupt-Shellcode. Enthält den Bootstrap-Code, den DFR-Resolver und Bereichsmarkierungen, an denen PICO-Module eingebunden werden. Wird direkt vom Loader aufgerufen.make object) – Eigenständige Code-Blöcke mit eigenen Code- und Datensektionen. Werden zur Laufzeit über PicoLoad() aus libtcg geladen. Jedes PICO hat einen Einstiegspunkt (go()), der über einen Funktionszeiger aufgerufen werden kann.MODULE$Funktion-Syntax (z.B. KERNEL32$VirtualAlloc) durch Aufrufe von resolve(mod_hash, func_hash) mittels ROR13-Hashing. Keine Importtabelle. Alle Zeichenkettenargumente (DLL-Namen, Funktionsnamen) werden als char-Arrays auf dem Stack aufgebaut, um Klartext im Binärformat zu vermeiden.entry_module, transport_module usw.) über die link-Direktive in der .spec-Datei eingebettet.{LoadLibraryA, GetProcAddress}, die an PicoLoad() übergeben wird, damit PICO-Module ihre eigenen DFR-Symbole auflösen können.C source → mingw-gcc → COFF objects → Crystal Palace link → raw PIC shellcode → loader (Exe/Dll/Svc)
Die .spec-Datei definiert, wie Crystal Palace alles verknüpft:
x64:
load "Bin/obj/main.x64.o" # Core PIC
make pic +gofirst
foreach %LIBS: mergelib %_ # Merge libtcg
load "Bin/obj/entry.x64.o" # Entry PICO
make object
load "Bin/obj/crypto.x64.o" # merge crypto into entry
merge
load "Bin/obj/packer.x64.o" # merge packer into entry
merge
export
link "entry_module" # link at section marker
load "Bin/obj/transport.x64.o" # Transport PICO
make object
mergelib "lib/LibWinHttp/..."
export
link "transport_module"
... # task_module, obfuscation_module
dfr "resolve" "ror13" # resolve all DFR symbols
export
Core PIC (main.c)
│
├── resolve() DFR-Brücke → libtcg Hash-Lookup
├── AllocateAndLoadModule() PicoLoad jedes PICO in gemeinsamen RWX-Bereich
│
└── ruft entry module go() mit Zeigern auf alle anderen Module auf
│
├── Entry Module (entry.c)
│ MvState, Check-in, Transaktion (RC4-Drahtformat), Aufgaben-Schleife
│
├── Transport Module (transport.c)
│ HTTP/HTTPS POST via LibWinHttp
│
├── Task Module (tasks.c)
│ Befehlsverteilung: whoami (0x30), sleep (0x20), exit (0x10)
│
└── Obfuscation Module (obfuscation.c)
Ekko-Schlaf — Timer-Queue-ROP-Kette, die den Modulspeicher
mit RC4 (SystemFunction033) während des Schlafs verschlüsselt
und beim Aufwachen entschlüsselt
┌─────────────────────────────┐
│ Gemeinsamer RWX-Bereich │ VirtualAlloc(PAGE_EXECUTE_READWRITE)
│ ├── Entry-Code │ PicoLoad → Code hier
│ ├── Transport-Code │
│ ├── Task-Code │
│ └── Verschleierungs-Code │
├─────────────────────────────┤
│ Entry-Daten (RW) │ PicoLoad → Daten hier (separate Allokation)
│ Transport-Daten (RW) │
│ Task-Daten (RW) │
│ Verschleierungs-Daten (RW) │
├─────────────────────────────┤
│ Core PIC (nach Start freigegeben) │ Original-Shellcode, vom Entry-Modul freigegeben
└─────────────────────────────┘
Der gemeinsame RWX-Bereich wird von Ekko während der Schlafzyklen ver-/entschlüsselt.
Der gesamte Verkehr wird mit RC4 (Stromchiffre, 16-Byte-Schlüssel) verschlüsselt.
Senden: [36B agent_id][RC4(payload)][16B key (nur beim ersten Check-in)]
Empfangen: [36B agent_id][RC4(response)]
src_beacon/Source/
├── main.c Core PIC — DFR-Resolver, Modulladung, Bootstrap
├── entry.c Entry PICO — Agentenstatus, Check-in, Aufgaben-Schleife, Transaktion
├── transport.c Transport PICO — HTTP POST via LibWinHttp
├── tasks.c Task PICO — whoami/sleep/exit-Verteilung
├── obfuscation.c Verschleierungs-PICO — Ekko-Schlaf (Timer-Queue-ROP + RC4)
├── crypto.c RC4-Stromchiffre (in Entry PICO eingebunden)
├── packer.c Binärpacker BE / Parser LE (in Entry PICO eingebunden)
└── includes/
├── config.h Build-Defines (UUID, Schlaf, Callback-Host/Port/URI/SSL)
├── crypto.h RC4-API
├── packer.h PackBuf / Parser-API
├── tcg.h Crystal Palace libtcg (PicoLoad, findModuleByHash usw.)
└── HTTP.h LibWinHttp-API
| Befehl |
|---|
x86_64-w64-mingw32-gcc (MinGW-Cross-Compiler)./setup.sh --ax ../AdaptixC2
whoami, sleep, exit-Befehle über die Adaptix-Konsole
| ID |
|---|
| Beschreibung |
|---|
whoami | 0x30 | Gibt COMPUTER\benutzername zurück |
sleep <sekunden> | 0x20 | Aktualisiert das Callback-Intervall |
exit thread|process | 0x10 | Beendet den Agenten |