Skip to content

sebafvs/bof-shell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bof-shell

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.


What it does

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.


Files

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

Build

Requires mingw-w64.

make

Or 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 -mconsole

Usage

With the standalone loader

loader.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"

With a C2 agent (Cobalt Strike compatible)

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);

How it works

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.


Notes

  • No CRT — BOFs cannot link against the standard library. String building is done manually without snprintf.
  • No printf — output goes through BeaconOutput, never printf.
  • Timeout — commands that hang are killed after 10 seconds via WaitForSingleObject.
  • Exit code — non-zero exit codes are reported via BeaconPrintf(CALLBACK_ERROR, ...).

Requirements

  • Windows x64
  • mingw-w64 cross-compiler
  • Cobalt Strike compatible C2 agent (or use the included loader for testing)

License

MIT

About

Beacon Object File (BOF) that executes shell commands in-process via cmd.exe /c and streams output back to the operator.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors