BOFs for Script Kiddies
Introduction
I hope I don’t sound like a complete n00b, but what or who or where is a BOF? All the cool kids are talking about it, and I just smile and nod. Is he the newest Crypto billionaire, or is a meetup for like-minded hackers, or is it some other 1337 slang?
I understand the confusion. Acronyms can be tricky and there is nothing like jargon to make one feel lost. However, I'm here yet again to provide guidance to all those Script Kiddies out there trying to keep up with the latest techniques. I'll attempt to answer all your basic questions about BOFs: who, what, where, when, and most importantly why, so at your next DefCon meetup you won’t feel quite so out-of-the-loop. Soon, you too will be using BOFs or at least correctly using the term.
What BOF?
Great. So, first things first, what is a BOF?
Before I explain what a BOF is, it helps to have a little background information. Microsoft Windows relies on many different file formats but probably none more important than the Portable Executable (PE) file format. Almost all executable files on a Windows machine follow the PE file format. In addition to this file format, we have the Common Object File Format (COFF). COFF describes the layout for object files which are the compiled binary files that are linked together to produce a PE binary. Therefore, COFF files are the result of compiling C/C++ files, AKA: your basic .o/.obj files.
These object files contain a COFF header, section headers, and raw data (Such as code, data, debug info, and relocations). Object files themselves are not executable as a standalone binary, but they do contain all the required information. This information is used by the linker to produce the executable binary. The operating system in turn uses this information when loading and executing the binary. In particular, the COFF header has several important fields (illustrated in the following table).
In the object file, following the COFF Header are the Section Headers. The Section Headers describe the layout, location, and characteristics of the Raw Data. This includes the data, code, and relocations. The Section Headers appear in a table and each table entry has the following format.
Now that we have an idea of what an object file and COFF are, we can discuss BOFs. BOFs are Beacon Object Files. They are a special type of object file that is designed and developed to work specifically with the Cobalt Strike framework. In particular, the Cobalt Strike agent, known as a Beacon, can load and execute these object files directly without the use of a linker and without using the operating system to load and run them. Essentially, the Beacon treats the object file as just a block of position-independent code and uses the information from the COFF Header and Section Headers to load the BOF into memory. In addition, the Beacon loader provides pointers to some of the Beacon internal APIs. Therefore, you can develop your C code as if you were going to be linked against these internal Beacon APIs. More specifically, BOFs have access to:
- Data parsing APIs used for passing arguments into the BOF functions
- BeaconDataExtract
- BeaconDataInt
- BeaconDataLength
- BeaconDataParse
- BeaconDataShort
- Output APIs used for returning output from the BOF functions
- BeaconPrintf
- BeaconOutput
- Format APIs used to build large or repeating output
- BeaconFormatAlloc
- BeaconFormatAppend
- BeaconFormatFree
- BeaconFormatInt
- BeaconFormatPrintf
- BeaconFormatReset
- BeaconFormatToString
- Internal Beacon APIs used to manipulate the current Beacon context
- BeaconUseToken
- BeaconRevertToken
- BeaconIsAdmin
- BeaconGetSpawnTo
- BeaconSpawnTemporaryProcess
- BeaconInjectProcess
- BeaconInjectTemporaryProcess
- BeaconCleanupProcess
- toWideChar
So, BOFs are just binary object files with some extra support for loading and running in memory. And with a little background in Windows object files, hopefully you have a better understanding of BOFs.
Who BOF?
OK, so BOFs sound pretty amazing. Who thought this up? Who should use it? Who should be afraid?
BOFs were created by the developers of Cobalt Strike for use with their Beacon payload. Therefore, you may think that BOFs are only for Cobalt Strike users, but that's not the case. With the success of BOFs and proliferation of examples, many developers created their own compatible frameworks for utilizing BOFs including supporting some of the internal APIs. In fact, TrustedSec’s very own Kevin Haubris created a more generic COFFLoader, which has been widely accepted as the de facto standard for integrating BOF support into third-party projects. Also, TrustedSec’s Kevin Clark has also since written a great look at Metasploit’s BOF loader extension for Meterpreter. So, who should care about BOFs? Everyone. And who should be afraid of BOFs? Anyone running a network.
When BOF?
This BOF thing is pretty cool. When did it start? When should I use them?
BOFs have been around for a few years now and are growing in popularity with every passing integration of a COFFLoader. BOFs were originally described and supported with the release of Cobalt Strike 4.1 in June of 2020. In my opinion, the next big step was the developers guide released by our own Christopher Paschen in July 2020, and then of course Kevin’s COFFLoader in February 2021.
So, with the timeline established, when should you actually use a BOF? BOFs are designed for post-exploitation operations. They allow you to run additional capabilities from within the payload/agent’s process space without the need to load a full DLL, drop a binary to disk, or inject into another process. BOFs can be used to provide situational awareness or to further access and affect the systems. BOFs can be used in conjunction with or instead of the standard commands you would run on target. So, when BOFs? When you want to expand your post-exploitation toolkit with some handy little helpers.
Where BOF?
I’m not sure if this makes sense, but… Where BOF?
The ‘Where’ of BOF is an interesting question. BOFs are binary objects that contain code and data. These objects are typically downloaded at runtime and loaded from memory. This means that the BOFs are not written to disk and reside within the current process’s memory space. The BOF loader is responsible for allocating, writing, and eventually freeing the memory for the BOF. The BOF loader is also responsible for input and output. So, I guess one answer to “Where BOF?” is all in the current process’s memory space.
If you're wondering where you can find an example of a BOF, or a whole treasure trove of BOFs for that matter, then I recommend looking at TrustedSec’s own repositories. We have one that provides situational awareness BOFs for gathering system information and one for performing remote operations to modify the system/network. In addition to our fine selection, there are plenty of other repositories. As the awareness and popularity grows, the number of BOFs only increases. So, check your favorite hacking forums and repositories, but always try to understand what and how the BOF is operating.
https://github.com/trustedsec/CS-Situational-Awareness-BOF
https://github.com/trustedsec/CS-Remote-OPs-BOF
Why BOF?
OK. The last question was just to fit the schema, but ‘Why BOF?’ is legit. Why do I care about BOFs. What advantage do they have over shellcode or some other post-ex bomb?
‘To BOF or not to BOF’, that is the question. Why should you use a BOF when there are plenty of other methods and tools? Well, BOFs have several advantages:
- BOFs allow you to rapidly extend an agent with new post-exploitation features.
- BOFs have fewer Indicators-of-Compromise (IoCs) than loading DLLs or invoking .NET assemblies or executing binaries or running scripts.
- BOFs are a lot smaller than a fully compiled and linked PE file.
- BOFs are easy to develop and rapidly prototype.
BOFs are great, but they do have some disadvantages:
- BOFs run in the same memory space as your agent, so if it crashes, then your agent crashes.
- BOFs are limited in their external dependencies, and calling external APIs can be a bit convoluted.
- BOFs are intended to run for short periods of time.
- BOFs probably won’t allow the use of global variables.
- Some linker features such as _CHKSTK_MS are not available.
Ultimately, I think that the pros outweigh the cons. BOFs provide a lot of functionality in a small package. They can be leveraged to provide quick capabilities while at the same time limiting the exposure of your agent through additional IoCs. The drawbacks are minimal, especially if you are using BOFs for their intended purpose: one-off commands that will return results quickly. You just need to know the limitations and advantages.
How BOF?
Now that I know the five W’s of BOFs, how do I actually BOF?
If you are already using Cobalt Strike or Metasploit or any of the other frameworks that support BOFs, then getting started operationally is quite simple: 1) Run BOF. 2) Profit. As previously mentioned, BOFs have become quite popular and there are numerous repositories of previously developed BOFs. The BOF technique makes it quite easy to plug-n-play any capability into your current framework/payload. The tricky part is understanding how to load and task each individual BOF, so please read your manuals!
If you want to move to the next level and create your own BOFs (and I highly recommend that you try), then you can start with Christopher Paschen’s blog, “A Developer’s Introduction to Beacon Object Files”. For more information on the internal APIs supported by Cobalt Strike, please refer to their documentation. Not all BOF loaders support the same internal APIs, so that is something that you need to be aware of. So, get reading, get coding, and good luck.
Conclusion
BOFs are definitely not the newest Crypto king or passing fad, and they are not as scary or confusing as they sound. BOFs are pretty slick and have drastically increased my arsenal. Thank you, TrustedSec.
BOFs are extremely useful tools in the battle of post-exploitation. We have covered the basic questions of BOFs including What, Why, Who, When, Where, and How. In the end, BOFs are just COFFs in disguise. They were originally envisioned by Cobalt Strike for their Beacon agent, but they have since been adapted and included in many agent frameworks. They are useful during the post-exploitation phase. The binary objects are loaded by the agent’s loader and located within the agent’s memory space. BOFs provide numerous advantages over typical post-exploitation tools including increased OPSEC and a smaller footprint. And now that you know all about BOFs, you can start using and developing to your heart’s content. Just make sure to always check your return values.
References
https://www.trustedsec.com/blog/coffloader-building-your-own-in-memory-loader-or-how-to-run-bofs/
https://www.trustedsec.com/blog/operators-guide-to-the-meterpreter-bofloader/
https://www.trustedsec.com/blog/a-developers-introduction-to-beacon-object-files/