You would like to execute BOFs written for Cobalt Strike in Brute Ratel C4? Look no further, we got you covered! CS2BR implements a compatibility-layer that make CS BOFs use the BRC4 API. This allows you to use the vast landscape that is BOFs in BRC4.
Please read about its caveats before using CS2BR.
As the BRC4 documentation on coffexec describes, porting CS BOFs to BR is a straight-forward task: all that needs to be done is replacing the name of CS's go
entrypoint to BRC4's coffee
and replacing CS's API calls to the BRC4 equivalents. For some simple API calls this is trivial (e.g. you can replace BeaconPrintf
with BadgetDispatch
).
However there are several sub-APIs in CS's BOF C API that make this a more elaborate task:
- The
Data Parser API
provides utilities to parse data passed to the BOF. BRC4 doesn't have an equivalent for this as arguments are passed to BOFs as simple strings (using thechar** argv, int argc
parameters in the entrypoint). - The
Format API
allows BOFs to format output in buffers for later transmission. BRC4 doesn't currently have an equivalent API. - The
Internal API
features several utilities related to user impersonation, privileges and process injection. BRC4 doesn't currently have an equivalent API.
CS2BR is not a silver bullet that solves the problem of CS and BRC4 BOF incompatibility. There are a couple of caveats one should consider when utilizing CS2BR:
- CS2BR (currently) works only on a source code level: if you want to patch a BOF that you don't have source code for, CS2BR won't be of use to you.
- Patching the compatibility layer into source code results in more code getting generated, thus increasing the size of the compiled BOF. Also note that the compatibility layer code can get flagged in the future.
- CS2BR does not (yet) support all of CS's BOF C API: namely the
Internal API
is populated with stubs only and won't do anything. This mainly concerns BOFs utilizing CS's user impersonation and process injection API calls. - While CS2BR allows you to pass parameters to BOFs, you'll still have to work out the number and type of parameters yourself by dissecting your BOF's CNA.
There are three steps to using CS2BR:
- Patching: Patch CS2BR compatibility-layer into BOF source code
- Compile the BOF as instructed by the BOF authors
- (Optionally)Parameters: Generate parameters to pass to a BOF
- Execute BOF using
coffexec
in BRC4
There are two options to patch BOF source code: you can either do this yourself of have the Python patching script do the job.
- Find the
beacon.h
file that contains the CS BOF C API definitions (ref. beacon.h) - Replace its contents with beacon_wrapper.h's contents.
- Find the file containing the
go
entrypoint. - Rename the
go
entrypoint tocsentry
- Append the contents of badger_stub.c to the file.
Run patch.py (requires Python 3):
usage: patch [-h] [--src SRC] [--beaconh BEACONH] [--entrypoint ENTRYPOINT] [--forcepatch] [--dry]
Patches Cobalt Strike BOF source code to be compatible with BruteRatel
options:
-h, --help show this help message and exit
--src SRC Directory of source code to patch (default: current working dir ,currently ".")
--beaconh BEACONH Name/pattern of or path to the headerfile(s) with Cobalt Strike beacon definitions to patch (default: "beacon.h")
--entrypoint ENTRYPOINT
Name or pattern of the source file that contains the Cobalt Strike "go" entrypoint (default: "*.c", so any C source file).
--forcepatch Force patching already patched files
--dry Dry-run: don't actually patch any files.
Example: ./patch.py --src /path/to/CS-Situational-Awareness-BOF
(to patch trustedsec's Situational Awareness BOFs)
CS's Data Parse API
allows passing arbitrary data to BOFs, including integers and binary blobs. BRC4 however can't pass arbitrary binary data to BOFs but only provides passing strings.
To workaround this, CS2BR's compatibility-layer takes base64 encoded input and feeds this to the Data Parse API
. However BRC4 doesn't feature aggressor scripts (CNA scripts) that query user inputs. CS2BR comes with encode_args.py that allows you to enter parameters and generates the base64 string you can pass to your BOF in BRC4.
For example, here a base64 string is built using encode_args.py
that can be consumed by the Data Parse API
through CS2BR:
./encode_args.py
Documented commands (type help <topic>):
========================================
addString addWString addint addshort exit generate help reset
BOF parameter encoder
CMD> addString localhost
CMD> generate
CgAAAGxvY2FsaG9zdAA=
CMD> exit
Alternatively, you can use encode_args.py
non-interactively by passing pairs of <type>:<value>
arguments to it, e.g.:
./encode_args.py "z:my first string" "Z:here's a wide-string" i:420 s:69
EAAAAG15IGZpcnN0IHN0cmluZwAqAAAAaABlAHIAZQAnAHMAIABhACAAdwBpAGQAZQAtAHMAdAByAGkAbgBnAAAApAEAAEUA
CS2BR didn't invent (most of) the concepts it uses. It utilizes code from the following sources:
- COFF Loader by trustedsec: Basis for the compatibility-layer and encode_args.py script
- Base64 C implementation by John Schember: Basis for the compatibility-layer's base64 decoding
- Brute Ratel's coffexec documentation
- Cobalt Strike's BOF documentation