A Beacon Object File (BOF) that executes a shell command via cmd.exe /c and returns the full output to the operator.
Blog post: Understanding and Writing BOFs Read the full write-up for a deep dive into how this BOF works, how BOFs are structured, and how the loader executes them.
Spawns cmd.exe /c <command> with stdout and stderr redirected into an anonymous pipe, reads all output, and sends it back through BeaconOutput. No disk writes, no visible console window.
| File | Purpose |
|---|---|
shell.c |
The BOF; compile to .o and load into your agent |
beacon.h |
Cobalt Strike compatible API, include in every BOF |
loader.c |
Standalone test loader, runs BOFs without a C2 |
Makefile |
Builds both shell.o and loader.exe |
Requires mingw-w64.
makeOr manually:
# BOF
x86_64-w64-mingw32-gcc -o shell.o -c shell.c -masm=intel -O0 -mno-stack-arg-probe -fno-stack-check
# Loader (for local testing)
x86_64-w64-mingw32-gcc -o loader.exe loader.c -Wall -O2 -mconsoleloader.exe shell.o str:"whoami /all"
loader.exe shell.o str:"net localgroup administrators"
loader.exe shell.o str:"ipconfig /all"
loader.exe shell.o str:"net user"
loader.exe shell.o str:"tasklist /v"beacon> inline-execute shell.o str:"whoami /all"
From an aggressor script:
$bof = openf(script_resource("shell.o"));
$data = readb($bof, -1);
closef($bof);
$args = bof_pack("z", "whoami /all");
beacon_inline_execute($bid, $data, "go", $args);
go(args)
└─ BeaconDataExtract() parse command from args blob
└─ CreatePipe() create anonymous pipe (read/write ends)
└─ CreateProcessA() spawn cmd.exe /c <command>
└─ hStdOutput = hWrite redirect stdout into pipe
└─ hStdError = hWrite redirect stderr into pipe
└─ CloseHandle(hWrite) close parent's write end
└─ critical: without this, ReadFile blocks after child exits
└─ ReadFile() loop read output and forward via BeaconOutput
└─ WaitForSingleObject() wait for process to finish (10s timeout)
└─ CloseHandle() cleanup
The write end of the pipe is closed in the parent process immediately after CreateProcess. If it stays open, ReadFile blocks indefinitely because the pipe still has an open writer — even after the child process exits.
- No CRT — BOFs cannot link against the standard library. String building is done manually without
snprintf. - No
printf— output goes throughBeaconOutput, neverprintf. - Timeout — commands that hang are killed after 10 seconds via
WaitForSingleObject. - Exit code — non-zero exit codes are reported via
BeaconPrintf(CALLBACK_ERROR, ...).
- Windows x64
mingw-w64cross-compiler- Cobalt Strike compatible C2 agent (or use the included loader for testing)
MIT