
Beacon Object File pour Cobalt Strike qui exécute des assemblys .NET dans le beacon avec des techniques d'évasion.
Beacon Object File pour Cobalt Strike qui exécute des assemblys .NET dans le beacon avec des techniques d'évasion.
┌──────────────────────────────────────────────────────────────────────────────┐
│ Cobalt Strike Beacon │
│ (Parent Process) │
└──────────────────────────────────┬───────────────────────────────────────────┘
│
│ beacon_inline_execute()
│ - Parse packed arguments
│ - Call go()
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ BOF Execute-Assembly Entry (go) │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Configuration Parsing │ │
│ │ • ProxyMethod (None/Draugr/Timer/RegWait) │ │
│ │ • AmsiEvasion (None/Patch/HWBP) │ │
│ │ • EtwEvasion (None/Patch) │ │
│ │ • PipeName, AppDomainName, Assembly bytes, Arguments │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ Framework Initialization │ │
│ │ • InitVxTable() - Resolve syscall numbers │ │
│ │ └─> NtProtectVirtualMemory, NtContinue, NtCreateEvent, │ │
│ │ NtSetEvent, NtWaitForSingleObject, NtClose │ │
│ │ • DraugrInit() - Setup synthetic stack frames │ │
│ │ └─> Locate RtlUserThreadStart, BaseThreadInitThunk │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ DLL Loading (ProxyLoadLibraryA) │ │
│ │ • amsi.dll, OleAut32.dll, mscoree.dll, User32.dll │ │
│ │ │ │
│ │ PROXY_NONE: LoadLibraryA() directly │ │
│ │ PROXY_DRAUGR: DRAUGR_API(LoadLibraryA) - spoofed stack │ │
│ │ PROXY_TIMER: CreateTimerQueue → Timer callback │ │
│ │ PROXY_REGWAIT: RegisterWaitForSingleObject → Event callback │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ AMSI Evasion Setup │ │
│ │ │ │
│ │ AMSI_PATCH: AMSI_HWBP: │ │
│ │ ┌─────────────────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ 1. Backup 4 bytes │ │ 1. Add VEH Handler │ │ │
│ │ │ 2. NtProtectVirtualMem │ │ 2. RtlCaptureContext │ │ │
│ │ │ (RW) │ │ 3. Set DR0 = AmsiScanBuffer │ │ │
│ │ │ 3. Write: │ │ 4. Enable DR7 breakpoint │ │ │
│ │ │ 48 31 C0 xor rax,rax│ │ 5. NtContinue (apply ctx) │ │ │
│ │ │ C3 ret │ │ │ │ │
│ │ │ 4. NtProtectVirtualMem │ │ On AmsiScanBuffer call: │ │ │
│ │ │ (restore) │ │ → #BP Exception │ │ │
│ │ └─────────────────────────┘ │ → VEH redirects to RET │ │ │
│ │ │ → RAX = 0 │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ ETW Evasion (if enabled) │ │
│ │ • NtProtectVirtualMemory(NtTraceEvent, RW) │ │
│ │ • Backup 4 bytes │ │
│ │ • Write: 48 31 C0 C3 (xor rax,rax; ret) │ │
│ │ • NtProtectVirtualMemory(restore protection) │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ Output Redirection Setup │ │
│ │ ┌──────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 1. CreateNamedPipeW(\\.\pipe\{CustomName}) → hPipe │ │ │
│ │ │ 2. CreateFileW(pipe path) → hFile │ │ │
│ │ │ 3. AllocConsole() + ShowWindow(SW_HIDE) → Hidden console │ │ │
│ │ │ │ │ │
│ │ │ 4. PEB Manipulation: │ │ │
│ │ │ • Backup: hCurrentStdOut = PEB->ProcessParameters->StdOut │ │ │
│ │ │ • Backup: hCurrentStdErr = PEB->ProcessParameters->StdErr │ │ │
│ │ │ • Redirect: PEB->StdOut = hFile │ │ │
│ │ │ • Redirect: PEB->StdErr = hFile │ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ CLR Hosting & Assembly Execution (ExecuteAssembly) │ │
│ │ ┌──────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 1. CLR Version Detection │ │ │
│ │ │ • Scan assembly bytes for "v2.0.50727" or "v4.0.30319" │ │ │
│ │ │ │ │ │
│ │ │ 2. CLR Initialization │ │ │
│ │ │ • CLRCreateInstance → ICLRMetaHost │ │ │
│ │ │ • GetRuntime(v2/v4) → ICLRRuntimeInfo │ │ │
│ │ │ • GetInterface → ICorRuntimeHost │ │ │
│ │ │ • Start() │ │ │
│ │ │ │ │ │
│ │ │ 3. AppDomain Management │ │ │
│ │ │ • GetDefaultDomain() → Default AppDomain │ │ │
│ │ │ • CreateDomain(CustomName) → Isolated AppDomain │ │ │
│ │ │ │ │ │
│ │ │ 4. Assembly Loading │ │ │
│ │ │ • Create SAFEARRAY (VT_UI1) with assembly bytes │ │ │
│ │ │ • SafeArrayAccessData → Copy assembly to safe array │ │ │
│ │ │ • CustomAppDomain->Load_3(safearray) → Load in memory │ │ │
│ │ │ │ │ │
│ │ │ 5. Argument Preparation │ │ │
│ │ │ • Parse space-delimited arguments │ │ │
│ │ │ • Create SAFEARRAY(VT_BSTR) for each argument │ │ │
│ │ │ • Wrap in VARIANT structure │ │ │
│ │ │ │ │ │
│ │ │ 6. Execution │ │ │
│ │ │ • Assembly->EntryPoint() → Get Main() MethodInfo │ │ │
│ │ │ • MethodInfo->Invoke_3(arguments) → Execute │ │ │
│ │ │ └─> Assembly writes to Console │ │ │
│ │ │ └─> Redirected to hFile → Named Pipe │ │ │
│ │ │ │ │ │
│ │ │ 7. Cleanup │ │ │
│ │ │ • Release COM interfaces (MethodInfo, Assembly, etc.) │ │ │
│ │ │ • UnloadDomain(CustomAppDomain) → Full unload │ │ │
│ │ │ • FreeLibrary(mscoree.dll) │ │ │
│ │ └──────────────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ Output Capture & Display │ │
│ │ • Restore PEB: StdOut/StdErr = original handles │ │
│ │ • Allocate buffer (0x10000 bytes) │ │
│ │ • ReadFile(hPipe) → Capture assembly output │ │
│ │ • BeaconPrintf(CALLBACK_OUTPUT, output) → Display to operator │ │
│ └────────────────────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────▼───────────────────────────────────────┐ │
│ │ Cleanup & Restoration │ │
│ │ • free(pAssemblyStdOut) │ │
│ │ • NtClose(hFile, hPipe) │ │
│ │ • FreeConsole() │ │
│ │ │ │
│ │ if (AMSI_PATCH): │ │
│ │ • RestoreAmsi() - Write original 4 bytes back │ │
│ │ │ │
│ │ if (AMSI_HWBP): │ │
│ │ • RemoveHwbp() - Clear debug registers │ │
│ │ • RemoveVectoredExceptionHandler(VehHandler) │ │
│ │ │ │
│ │ if (ETW_PATCH): │ │
│ │ • RestoreEtw() - Write original 4 bytes back │ │
│ │ │ │
│ │ • Restore PEB: StdOut/StdErr = original │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
│ Return to Beacon
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ Beacon continues execution │
│ (BOF memory cleaned up) │
└──────────────────────────────────────────────────────────────────────────────┘
| Méthode | Description |
|---|---|
None | Appels API directs |
Draugr | Appels API avec spoofing de pile via Draugr |
| Méthode | Description |
|---|---|
None | Aucun contournement AMSI |
Patch | Patch mémoire de AMSI!AmsiScanBuffer (xor rax,rax; ret) |
HWBP | Hook par point d'arrêt matériel sur AMSI!AmsiScanBuffer via VEH |
| Méthode | Description |
|---|---|
None | Aucun contournement ETW |
Patch | Patch mémoire de NTDLL!NtTraceEvent (xor rax,rax; ret) |
| Paramètre | Description | Exemple |
|---|---|---|
| PipeName | Nom du pipe nommé pour capturer la sortie de l'assembly | P1p3N4m3 |
| AppDomain | Nom d'AppDomain .NET personnalisé pour l'isolation de l'assembly | Tot4lL3g1t |
LoadLibraryA("amsi.dll") → Direct call
DRAUGR_API(LoadLibraryA, "amsi.dll")
│
├─ Synthetic Stack Construction
├─ Return Address Spoofing
└─ Indirect Execution
CreateTimerQueue() → CreateTimerQueueTimer(
callback = LoadLibraryA,
parameter = "amsi.dll",
dueTime = 100ms
) → Wait → DeleteTimerQueueEx()
CreateEvent() → RegisterWaitForSingleObject(
event,
callback = LoadLibraryA,
context = "amsi.dll"
) → SetEvent() → UnregisterWait()
Before Patch: After Patch:
AmsiScanBuffer: AmsiScanBuffer:
4C 8B DC mov r11, rsp 48 31 C0 xor rax, rax
49 89 5B 08 mov [r11+8], rbx C3 ret
... ...
Result: All scans return S_OK (clean)
Méthode :
xor rax, rax; retSetup:
1. AddVectoredExceptionHandler
2. RtlCaptureContext
3. Set DR0 = AmsiScanBuffer address
4. Enable DR7 breakpoint flag
5. NtContinue (apply context)
Execution Flow:
AmsiScanBuffer called
│
▼
#BP Exception (EXCEPTION_SINGLE_STEP)
│
▼
VEH Handler intercepts
│
├─ Verify RIP == AmsiScanBuffer
├─ Set RIP = FindRetInstruction(AmsiScanBuffer)
├─ Set RAX = 0 (S_OK)
└─ Set TF (Trap Flag)
│
▼
Return with RAX=0
Before: After:
NtTraceEvent: NtTraceEvent:
4C 8B D1 mov r10, rcx 48 31 C0 xor rax, rax
B8 XX XX mov eax, syscall C3 ret
Standard Assembly (No BOF): BOF Execute-Assembly:
Assembly → Console.WriteLine 1. Create \\.\pipe\{name}
│ │
▼ ▼
Output lost 2. Open pipe as file handle
│
▼
3. Redirect PEB handles:
• StdOut → pipe
• StdErr → pipe
│
▼
4. Execute assembly
│
▼
5. ReadFile(pipe)
│
▼
6. BeaconPrintf → Operator
Changements de protection mémoire :
NtProtectVirtualMemory journalisés via EtwTiLogReadWriteVmamsi.dllntdll.dllDétection : Les changements de protection mémoire sur les modules chargés sont de forts indicateurs.
Création de pipe nommé :
NtCreateFile avec un chemin \\.\pipe\* visible par les pilotes minifilterChargement de modules :
LdrLoadDll journalisés par les pilotes noyau EDRManipulation du contexte de thread (méthode HWBP) :
AllocConsole + ShowWindow(SW_HIDE))Cobalt Strike → Script Manager → Load → BOF_ExecuteAssembly.cna
Menu: Additionals postex → Execute-Assembly Config

BOF_ExecuteAssembly --assembly /tmp/Ghostpack-CompiledBinaries/Rubeus.exe --args help

beacon> help BOF_ExecuteAssembl

Avec Dockerfile :
sudo docker build -t ubuntu-gcc-13 .
sudo docker run --rm -it -v "$PWD":/work -w /work ubuntu-gcc-13:latest make
Ou, si vous avez nasm, make et mingw-w64 (compatible avec gcc-13) sur votre système :
make
Sortie : Bin/BOF_ExecuteAssembly.o
Regwait | Exécution de callback RegisterWaitForSingleObject |
Timer | Exécution de callback de file de temporisation |
| Technique | Contournements |
|---|
| Indirect Syscalls | Hooks API userland (EDR/AV) |
| Draugr Stack Spoofing | Outils d'inspection de la pile d'appels |
| AMSI Patch/HWBP | Analyse des assemblys .NET |
| ETW Patching | Surveillance basée sur les événements |
| Proxy DLL Loading | Surveillance des stackframes LoadLibrary |
| Named Pipe Malleable | Surveillance des pipes |
| Custom AppDomain | Surveillance de l'AppDomain par défaut |