
File Oggetto Beacon per Cobalt Strike che esegue assembly .NET in beacon con tecniche di evasione.
Beacon Object File per Cobalt Strike che esegue assembly .NET nel beacon con tecniche di evasione.
┌──────────────────────────────────────────────────────────────────────────────┐
│ 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) │
└──────────────────────────────────────────────────────────────────────────────┘
| Metodo | Descrizione |
|---|---|
None | Chiamate API dirette |
Draugr | Chiamate API con stack spoofato tramite Draugr |
| Metodo | Descrizione |
|---|---|
None | Nessun bypass AMSI |
Patch | Patch in memoria di AMSI!AmsiScanBuffer (xor rax,rax; ret) |
HWBP | Hook hardware breakpoint su AMSI!AmsiScanBuffer tramite VEH |
| Metodo | Descrizione |
|---|---|
None | Nessun bypass ETW |
Patch | Patch in memoria di NTDLL!NtTraceEvent (xor rax,rax; ret) |
| Parametro | Descrizione | Esempio |
|---|---|---|
| PipeName | Nome della named pipe per catturare l'output dell'assembly | P1p3N4m3 |
| AppDomain | Nome personalizzato dell'AppDomain .NET per isolamento dell'assembly | Tot4lL3g1t |
LoadLibraryA("amsi.dll") → Chiamata diretta
DRAUGR_API(LoadLibraryA, "amsi.dll")
│
├─ Costruzione stack sintetico
├─ Spoofing dell'indirizzo di ritorno
└─ Esecuzione indiretta
CreateTimerQueue() → CreateTimerQueueTimer(
callback = LoadLibraryA,
parameter = "amsi.dll",
dueTime = 100ms
) → Wait → DeleteTimerQueueEx()
CreateEvent() → RegisterWaitForSingleObject(
event,
callback = LoadLibraryA,
context = "amsi.dll"
) → SetEvent() → UnregisterWait()
Prima della Patch: Dopo la 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
... ...
Risultato: Tutte le scansioni restituiscono S_OK (pulito)
Metodo:
xor rax, rax; retSetup:
1. AddVectoredExceptionHandler
2. RtlCaptureContext
3. Imposta DR0 = indirizzo di AmsiScanBuffer
4. Abilita flag breakpoint DR7
5. NtContinue (applica contesto)
Flusso di esecuzione:
Chiamata a AmsiScanBuffer
│
▼
Eccezione #BP (EXCEPTION_SINGLE_STEP)
│
▼
Intercettata dal gestore VEH
│
├─ Verifica RIP == AmsiScanBuffer
├─ Imposta RIP = FindRetInstruction(AmsiScanBuffer)
├─ Imposta RAX = 0 (S_OK)
└─ Imposta TF (Trap Flag)
│
▼
Ritorna con RAX=0
Prima: Dopo:
NtTraceEvent: NtTraceEvent:
4C 8B D1 mov r10, rcx 48 31 C0 xor rax, rax
B8 XX XX mov eax, syscall C3 ret
Assembly standard (senza BOF): BOF Execute-Assembly:
Assembly → Console.WriteLine 1. Crea \\.\pipe\{nome}
│ │
▼ ▼
Output perso 2. Apri pipe come file handle
│
▼
3. Reindirizza handle PEB:
• StdOut → pipe
• StdErr → pipe
│
▼
4. Esegui assembly
│
▼
5. ReadFile(pipe)
│
▼
6. BeaconPrintf → Operatore
Modifiche alla Protezione della Memoria:
NtProtectVirtualMemory registrate tramite EtwTiLogReadWriteVmamsi.dllntdll.dllRilevamento: Le modifiche alla protezione della memoria su moduli caricati sono forti indicatori.
Creazione Named Pipe:
NtCreateFile con percorso \\.\pipe\* visibile ai driver minifilterCaricamento Moduli:
LdrLoadDll registrati dai driver del kernel EDRManipolazione del Contesto del Thread (metodo 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

Con Dockerfile:
sudo docker build -t ubuntu-gcc-13 .
sudo docker run --rm -it -v "$PWD":/work -w /work ubuntu-gcc-13:latest make
Oppure, se hai nasm, make e mingw-w64 (compatibile con gcc-13) sul sistema:
make
Output: Bin/BOF_ExecuteAssembly.o
Regwait | Esecuzione tramite callback RegisterWaitForSingleObject |
Timer | Esecuzione tramite callback Timer Queue |
| Tecnica | Cosa Bypassa |
|---|
| Syscall Indirette | Hook API in userland (EDR/AV) |
| Stack Spoofing Draugr | Strumenti di ispezione call stack |
| Patch AMSI / HWBP | Scansione assembly .NET |
| Patch ETW | Monitoraggio basato su eventi |
| Caricamento DLL Proxy | Monitoraggio stack frame di LoadLibrary |
| Named Pipe Malleabile | Monitoraggio pipe |
| AppDomain Personalizzato | Monitoraggio dell'AppDomain predefinito |