Creating Beacon Object Files (BOFs) allows operators to extend the functionality of a C2 framework, though their development may sometimes involve hidden complexities that only become apparent after the BOF is executed. Today, we introduce a BOF linting tool to address some of the common pitfalls.
BOFs are lightweight, in-memory modules used in Cobalt Strike and other post-exploitation/C2 frameworks such as Outflank C2 and Core Impact. They are object files produced by a C compiler (COFF). Cobalt Strike parses this file and acts as a linker and loader for its contents. This approach allows you to write code for use in Beacon, without tedious gymnastics to manage strings and dynamically call Win32 APIs.
BOFs are powerful and flexible, but their minimalistic design inherently comes with strict constraints. For example, a lot of common functions (e.g., strlen, strcmp
, etc.) are not directly available to you via a BOF as the library these functions are defined in is not linked against. Instead, the BOF standard allows you to call external functions via Dynamic Function Resolution (DFR).
The Cobalt Strike team has published excellent documentation materials and support tools: BOF manual, BOF-template, and, most recently, BOF-VS. BOFs are usable in all frameworks with a BOF loader.
Within the OST offering, we provide various tools via BOF. We want BOFs to work across different frameworks, which means we need compatibility with the various available BOF loaders. Loaders might differ in implementation each with their own limitations.
If you have developed a BOF before, you might have encountered that BOF development can have subtle pitfalls. Furthermore, some issues can often only be noticed at runtime, instead of during development.
Inspired by the excellent primers like BOFs for Script Kiddies and A Developer’s Introduction to Beacon Object Files, we’ve built boflint
: a tool designed to make the BOF development lifecycle smoother and less error-prone.
BOF Limitations and Loader Limitations
BOFs impose inherent limitations, and compiler quirks (gcc vs. MSVC) along with loader differences can further affect the proper functioning of your BOF.
When the BOF loader parses the COFF file, it performs section handling, symbol handling, and relocation handling. Issues can arise during any of these stages:
- Section handling:
- E.g. not all loaders correctly parse sections like
.bss
(used for zero-initialized/unitialized globals). This might require workarounds in your code.
- E.g. not all loaders correctly parse sections like
- Symbol handling:
- E.g. references to unresolved symbols (for example, usage of
printf
instead ofBeaconPrintf
). This might also happen transparently when the compiler adds in references tomemset/memmove
when using struct initializers. Similarly, when you attempt to use STL functions, the compiler will automatically add symbol references.
- Every function or symbol used in a BOF must be explicitly resolvable by the loader. Undefined or unresolvable imports may cause the BOF to fail at runtime.
- E.g. references to unresolved symbols (for example, usage of
- Relocation handling:
- E.g. most loaders only implement a subset of relocation types.
By reviewing the section, symbol and relocation information in a BOF after compilation, we can identify potential issues early – before running the BOF.
Enter BOF Linting
To help identify potential BOF issues we developed boflint
: a post-compilation linter for BOF files. boflint
analyzes your BOF for common errors before running it in an implant, preventing worst-case scenarios such as crashing the implant.
boflint
identifies several key issues:
- Relocation types: Ensures only supported relocation types are used.
- Entry point validation: Verifies the presence of the mandatory
go
orsleep_mask
function. - Import resolution: Flags undefined or unresolvable imports.
- Large stack variables: Detects large stack variable usage that results in an unresolvable import for stack probing.
- Exception handling: Warns against the use of exception handling, which is unsupported in BOFs.
Several common issues can be detected by the linter by verifying the section, symbol and relocation information in the BOF. Different BOF loaders handle edge cases differently. boflint
currently supports validation for BOFs targeting Cobalt Strike, OC2 and Core Impact.
Using boflint
in Your Workflow
In 2023, the Cobalt Strike team released a Visual Studio BOF template (BOF-VS), which was aimed at simplifying BOF development and resolving many of the common issues that arise in BOF development.
We have worked closely with the Cobalt Strike team to incorporate boflint
directly into BOF-VS’s workflow. Now when building a BOF in Visual Studio, the linter will automatically check your BOF for known issues, such as unresolved import symbols, as highlighted in the screenshot below.

boflint
highlighting a unresolved imported symbol error.If you compile BOFs in other environments, integrating boflint
into your workflow is simple. The idea is to run boflint
after compiling your BOF, so you could add it to your Makefile.
For example, a Makefile might look like this:
bof
: x86_64-w64-mingw32-gcc -o example.o example.c
boflint.py example.o
Get Started with Linting
At first glance, some of the issues boflint
catches might seem niche or minor. But as any seasoned BOF developer can attest, some problems like an unsupported relocation or an unresolvable import can be very subtle and catch you off guard.
By using boflint
you can catch issues early during development, and increase the likelihood of BOFs working across different loaders.
The boflint
tool has support for different loader types and allows to display verbose output.

Within Fortra we have multiple products that can load a BOF-file, collaboration across the various product teams has resulted in specific BOF loading checks for Cobalt Strike, Outflank C2 and Core Impact. We hope linting will allow developers to more easily pinpoint BOF loading quirks and also enable better bug reports to bring loader support to the next level. If you’re not an OST customer but are interested in learning more about BOF loading and other ways to use OST and Cobalt Strike together, we recommend scheduling an expert led demo to learn more.
Ready to lint your BOFs? Use the BOF Visual Studio Template or grab the standalone boflint.py file from the repository!