
BOF pour exécuter un PE dans Cobalt Strike Beacon sans création de console
BOF RunPE est un fichier Beacon Object pour Cobalt Strike qui exécute des fichiers PE entièrement en mémoire dans le processus du beacon. Contrairement au fork&run traditionnel, aucun processus enfant n'est créé, aucune console n'est ouverte et aucun tube n'est utilisé – toute la sortie est capturée via un hooking IAT et redirigée vers la console du beacon.
Architecture : x64 uniquement
┌──────────────────────────────────────────────────────────────┐
│ Cobalt Strike Beacon │
│ (Current Process) │
└────────────────────────┬─────────────────────────────────────┘
│
│ beacon_inline_execute()
│
▼
┌──────────────────────────────────────────────────────────────┐
│ BOF RunPE │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ VxTable + Draugr Initialization │ │
│ │ (Syscall Resolution + Stack Spoofing) │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────────┐ │
│ │ PE Mapping │ │
│ │ - Section Copy - IAT Patching (with hooks) │ │
│ │ - Relocations - Memory Protection │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────────┐ │
│ │ Thread Execution │ │
│ │ - Spoofed Start Address │ │
│ │ - RIP Hijacking to Entry Point │ │
│ │ - Output Redirection via Hooks │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
printf/WriteConsoleLe comportement du BOF peut être modifié dans Additionals postex -> RunPe Config

| Méthode | Description |
|---|---|
None | Appels API directs |
Draugr | Appels API avec spoofing de pile |
Regwait | Callback RegisterWaitForSingleObject |
Timer | Callback Timer Queue |
| Méthode | Description |
|---|---|
Heap | Tas privé via RtlCreateHeap avec Draugr |
VirtualAlloc | NtAllocateVirtualMemory avec Draugr |
Module stomping | Écrase la section .text d'une DLL légitime |
| Option | Description |
|---|---|
AllocRWX | Allouer en RWX (vs transition RW→RX) |
UnhookNtdll | Remplacer la section .text de ntdll.dll par une copie fraîche du disque |
Timeout | Délai d'exécution en millisecondes (0 = infini) |
StompModule | Chemin de la DLL pour le module stomping (ex. chakra.dll) |
| Option | Description |
|---|---|
ModuleName | Module légitime pour l'adresse de départ (ex. Kernel32.dll) |
ProcedureName | Nom de la fonction dans le module (ex. BaseThreadInitThunk) |
Offset | Décalage depuis le début de la fonction |
Toute la sortie du PE est redirigée vers la console du beacon via des hooks IAT. Aucune fenêtre de console ni tube nommé n'est créé.
| Fonction hookée | Cible |
|---|---|
GetCommandLineA/W | Retourne des arguments usurpés |
__getmainargs / __wgetmainargs | Initialisation des arguments CRT |
printf / wprintf | Redirection vers BeaconPrintf |
WriteConsoleA/W | Redirection vers BeaconPrintf |
__stdio_common_vfprintf | Fonctions d'impression UCRT |
ExitProcess / exit | Converti en ExitThread |
| Technique | Contourne |
|---|---|
| Syscalls indirects | Hooks API en espace utilisateur (EDR/AV) |
| Draugr Stack Spoofing | Inspection de la pile d'appels |
| Usurpation du démarrage de thread | Analyse de l'adresse de début de thread |
| Module Stomping | Détection de mémoire sans sauvegarde |
| Allocation en tas privé | Surveillance de VirtualAlloc |
| Déhooking de Ntdll | Écriture de ntdll du disque sur celui en mémoire |
| Hooking IAT (sans tubes) | Surveillance des tubes nommés |
NtGetContextThread / NtSetContextThread :
Opérations mémoire :
DONT_RESOLVE_DLL_REFERENCES (si l'allocateur est le module stomping)Cobalt Strike → Script Manager → Load → BOF_RunPe.cna
beacon> runpe /chemin/vers/binaire.exe --arg1 valeur1

beacon> help runpe

Nécessite GCC 13 (mingw-w64). Utiliser le Dockerfile fourni :
sudo docker build -t ubuntu-gcc-13 .
sudo docker run --rm -it -v "$PWD":/work -w /work ubuntu-gcc-13:latest make
Sortie : Bin/runpe.o
| Limitation | Description |
|---|---|
| CET | La technologie Control-flow Enforcement peut bloquer les trames de pile synthétiques |
| x64 Only | Pas de support x86/WoW64 |
| Kernel Visibility | La création de threads est visible par les callbacks du noyau |
| .NET | Les exécutables managés ne sont pas supportés |
Windows Native API Programming par Pavel YosifovichWindows Internals, Part 1 par Pavel YosifovichWindows Internals, Part 2 par Andrea Allievi