Cobalt Strike 4.9 is now available. This release sees an overhaul to Cobalt Strike’s post exploitation capabilities to support user defined reflective loaders (UDRLs), the ability to export Beacon without a reflective loader which adds official support for prepend-style UDRLs, support for callbacks in a number of built-in functions, a new in-Beacon data store and more.  

We intend to publish a few follow-up blog posts over the next couple of weeks to provide more detail on some of the changes in this release, so please keep your eye on the blog for those updates. If you haven’t subscribed to the technical mailing list for blog updates, it is worth considering doing that as well so that you don’t miss anything!  

Post-Exploitation Overhaul

Cobalt Strike’s post-exploitation capabilities have been given an overhaul, with support for prepend-style User Defined Reflective Loaders being added to the following post-exploitation DLLs: 

  • browserpivot 
  • hashdump 
  • invokeassembly 
  • keylogger 
  • mimikatz 
  • netview 
  • portscan 
  • powershell 
  • screenshot 
  • sshagent 

A new Aggressor Script hook, POSTEX_RDLL_GENERATE, has been added in order to implement this change, and replace the default reflective loader with a UDRL. Full details on this new hook and how it is used can be found in the documentation.  

It is important to note that UDRLs for Beacon payloads and post-exploitation payloads are very similar but have some subtle differences. Information on those differences, that relate to the loader entry function, the DLL’s entry point, the RDATA_SECTION pointer argument and the obfuscation start offset can be found in the documentation and should be carefully reviewed prior to making your own changes in this area. 

You can find an example implementation of a post-exploitation loader in the UDRL-VS kit in the Cobalt Strike Arsenal Kit. 

A new Malleable C2 option, post-ex.cleanup, has been added to specify whether or not to clean up the post-exploitation reflective loader memory when the DLL is loaded. We have also added the post-ex.transform-x64 and post-ex.transform-x86 blocks to the post-ex Malleable C2 block. Both new blocks support the strrep option, which replaces a string in all post-exploitation DLLs, and strrepex which replaces a string within a specific post-exploitation DLL. Valid DLL names are BrowserPivot, ExecuteAssembly, Hashdump, Keylogger, Mimikatz, NetView, PortScanner, PowerPick, Screenshot, and SSHAgent. For example:  

post-ex { 
    # cleanup the post-ex UDRL memory when the post-ex DLL is loaded  
    set cleanup "true"; 
    transform-x64 { 
        # replace a string in the port scanner dll 
        strrepex "PortScanner" "Scanner module is complete" "Scan is complete"; 

        # replace a string in all post exploitation dlls 
        strrep "is alive." "is up."; 

    transform-x86 { 
        # replace a string in the port scanner dll 
        strrepex "PortScanner" "Scanner module is complete" "Scan is complete"; 

        # replace a string in all post exploitation dlls 
        strrep "is alive." "is up."; 

You can find more information on this change in the documentation. 

Export Beacon Without A Reflective Loader 

Beacon can now be used without the exported reflective loader function when using UDRLs. This change also improves support for prepend-style UDRLs.  

The BEACON_RDLL_SIZE Aggressor Script hook is called when Beacon is being prepared and can now be used to remove the entire reflective loader space from the Beacon DLL. By returning a value of “0”, a Beacon without the reflective loader is passed to the BEACON_RDLL_GENERATE and BEACON_RDLL_GENERATE_LOCAL hooks. For example: 

# ------------------------------------ 
# $1 = DLLfilename 
# $2 = arch 
# ------------------------------------ 

    warn("Running 'BEACON_RDLL_SIZE' for DLL " .$1. " with architecture " .$2);    

As a result of this change, the default BEACON_RDLL_SIZE return value has been changed from 0 to 5.  

Callback Support

We have had a number of requests from our users to make it easier to process the results of certain function calls. This is challenging due to the asynchronous nature of Cobalt Strike’s communications, but this has been addressed in this release by the addition of callbacks for a number of built-in functions. Callbacks are triggered following a response to a command by Beacon, and also when dealing with custom dialogs via dialog input and action button clicks.  

Support for callbacks has been added to the following Aggressor Script functions: 

  • bnet 
  • beacon_inline_execute 
  • binline_execute 
  • bdllspawn 
  • bexecute_assembly 
  • bhashdump 
  • bmimikatz 
  • bmimikatz_small 
  • bportscan 
  • bpowerpick 
  • bpowershell 
  • bpsinject 

In general, there are three types of techniques that can be used to deal with callbacks – anonymous closure, named closure, and lambda closure. 

An anonymous closure is useful when you want to keep a small amount of code inline with the caller. For example, logging output from a BOF to the Beacon console would look like this: 

alias cs_example { 
    # User setup code removed for brevity      
    beacon_inline_execute($bid, $data, "go", $args, { blog($1, $2); }); 

A named closure is useful when you have a lot of code that you may want to reuse. In this example, we create a closure named “bof_cb”, which is executed when data is returned by the BOF: 

 $1 - bid, $2 - result, $3 - info map 
sub bof_cb { 
    # User defined code removed for brevity 

alias cs_example { 
    local('$bid $data $args'); 
    # User setup code removed for brevity 
    beacon_inline_execute($bid, $data, "go", $args, &bof_cb); 

A lambda closure is useful when you want to pass variables that would not be in scope using the previous examples. This example demonstrates how you are able to access the $test_num variable, which is in the scope of the cs_example alias: 

# $1 - bid, $2 – result, $3 - infomap, $4 - test_num 
sub bof_cb{ 
    # User defined code removed for brevity 

alias cs_example { 
    local('$bid $file $test_num'); 
    # User setup code removed for brevity  
    binline_execute($bid, $file, $test_num, lambda({ bof_cb($1, $2, $3, $test_num); }, \$test_num); 

Examples can also be found in a public Cobalt Strike GitHub repository, found here

Beacon Data Store

Along similar lines to the token store in the 4.8 release, we have added a Beacon Data Store that allows you to store BOFs and .NET assemblies in Beacon’s memory, allowing the stored items to be executed multiple times without having to resend the item. The Cobalt Strike client will automatically detect whether an object to be executed is already in the data store so there is nothing additional that you need to do on your part once an object is stored. Stored entries are masked by default, only becoming unmasked when used. It is also possible to store generic files in the data store which will be available for BOFs to use.  

The default data store size is 16 entries, although this can be modified by configuring the stage.data_store_size option within your Malleable C2 profile.  

data-store load [bof|dotnet|file] <name> [path] stores an item in the store. 
data-store unload [index] removes a stored item.
data-store list lists all items available in the data store. 

A number of BOF API functions have also been added to allow you to access and protect items that are stored in the Beacon Data Store:  

BeaconDataStoreGetItem(size_t index) returns a pointer to the specified item. The function will return NULL if no entry exists at the specified index. 
BeaconDataStoreProtectItem(size_t index) obfuscates a specific item in the Beacon Data Store. 
BeaconDataStoreUnprotectItem(size_t index) deobfuscates a specific item in the  Beacon Data Store. 
BeaconDataStoreMaxEntries() returns the maximum size of the Beacon Data Store. 

Beacon User Data 

Beacon User Data is a C structure that allows Reflective Loaders to pass additional data to Beacons. It is passed as a pointer to Beacon by calling Beacon’s DllMain function with a custom reasoning known as DLL_BEACON_USER_DATA (0x0d). This must be passed to Beacon before the standard DLL_PROCESS_ATTACH reason is invoked. It is not required to keep the structure in memory after the DLL_BEACON_USER_DATA call because Beacon copies necessary values from it during the call.  

A version number is contained in the structure to ensure backward compatibility between different versions of Beacons and Reflective Loaders. Newer Beacons will therefore be able to handle and utilize older Beacon User Data structures without crashing.  

Beacon User Data also allows a Reflective Loader to resolve and pass system call information to Beacon, overriding Beacon’s default system call resolver. The SYSCALL_API_ENTRY structure is used for each supported system call. The SYSCALL_API structure holds these entries. Each entry contains information on the jump address (depending on system architecture), the system call number and the address of the corresponding Nt* function. The jump address and system call number are required for indirect system calls, whilst the function address is required for direct system calls. If values are not specified, Beacon will fall back to the corresponding WinAPI call. If the system calls fields in the USER_DATA structure points to NULL, then the system call information is skipped.  

Beacon User Data also allows a Reflective Loader to pass a small (32 bytes) data buffer to Beacon, allowing users to specify and pass their own custom data. BOFs can retrieve a pointer to this data with the BeaconGetCustomUserData function.  

You can download beacon_user_data.h here and you can find a usage example in the UDRL-VS, which can be found in the Arsenal Kit.  

WinHTTP Support

Up until now, Beacon’s HTTP(S) listener has used the WinInet library by default. Based on feedback from a number of users, support for the WinHTTP library has been added.  

A new Malleable C2 group, .http-beacon, has been created. Additionally, a .http-beacon.library option has been added to allow you to set the default library used when creating a new HTTP(S) listener. Valid values are wininet and winhttp. If the new Malleable C2 profile option is not specified, new HTTP(S) listeners will continue to default to WinInet.  

The new http-beacon Malleable C2 profile group also supports variants, which can then be assigned to listeners. For example:  

http-beacon { 
    set library "winhttp"; 

http-beacon "httplib-wininet" { 
    set library "wininet"; 

http-beacon "httplib-winhttp" { 
    set library "winhttp"; 

The default value in the Malleable C2 profile can be overridden via the new HTTP library option in the stageless payload generator, generate all payloads, and windows executable dialogs. For example:  

Support has also been added in a number of Aggressor Script functions, which have been updated to add support for the new optional library parameter. When the parameter is not specified, the default library value is resolved from the listener definition. When specified, the value must be either an empty string, $null, wininet or winhttp. The following functions have been updated to support this additional parameter: 

  • payload 
  • payload_local 
  • artifact_payload 
  • all_payloads 

Host Profile Support for HTTP(S) Listeners 

Cobalt Strike’s HTTP(S) processing has limitations. Whilst we will be reviewing this and plan to make other, impactful changes in a future release, we have identified and addressed a couple of limitations in the scope of this release. Specifically, up to now, callback host names are assigned to a single URI when the Beacon payload is generated, and HTTP(S) parameters and headers are defined at a profile or variant level. This means that all HTTP(S) traffic to that host looks very similar.  

We have addressed these limitations by adding a new Malleable C2 profile group – http-host-profiles. This allows you to define HTTP characteristics (URI, headers and parameters) that will be used for HTTP(S) communications for a specific host name. Dynamic (randomly selected) values are supported. Variants are used to define host profiles for multiple host names.

Dynamic data is surrounded by square brackets and supports a list of values (up to 32) separated by a pipe character. A single value will be randomly selected from the list of dynamic data values, per request. It can also be embedded in static data, randomising part of the string.

The host-name field is a fixed string that links the host profile to the matching HTTP Hosts field on the HTTP(S) listener dialog. 

Up to 10 values are supported for the parameter and header values in a single host profile get/post definition. Both options support dynamic data syntax. Parameters and headers defined in a host profile are added in addition to any parameters and headers defined in the applicable default or variant profile.  

Beacons support up to 8 host profile definitions per listener. A generated Beacon has space for 1024 bytes of host profile data in total. If multiple hosts are defined then they must all fit within 1024 bytes. 

You can find a Malleable C2 profile with an example http-host-profiles block here.

Inter-Client Communications 

This release sees the addition of Aggressor Script support for sending and receiving data between Cobalt Strike clients. Up until this point, the only way to share data between clients was via Cobalt Strike’s Event Log – for example, @Octoberfest73’s MemFiles uses this approach to share data between clients and this is an excellent example use case for this new feature.  

Three new Aggressor Script functions have been added to facilitate the firing and consumption of custom events:  

custom_event is used to broadcast a custom event to all Cobalt Strike clients.
Arguments are:  
$1 – the topic name 
$2 – the event data 

custom_event_private is used to send a custom event to a single, specific Cobalt Strike client.
Arguments are:
$1 – who to send the custom event to 
$2 – the topic name 
$3 – the event data 

custom_event_<topic-name> is fired when a client receives a custom event from another client.
Arguments are:
$1 – who sent the custom event 
$2 – the event data 
$3 – the time the event was sent 

BOF Updates 

A feature that we’ve had on our backlog for quite some time is to add a key/value store to Beacon, which is intended to be used as a persistent store between BOF executions. As we have had recent customer requests along those lines, we figured that now would be a good time to address this.  

Three new APIs have been added to Beacon to support this key/value store:  
BeaconAddValue(const char * key, void * ptr) allows you to add a memory address to a key. 
BeaconGetValue(const char * key) allows you to retrieve the memory address associated with a key. 
BeaconRemoveValue(const char * key) allows you to remove the key. Note that this will not do any memory clean-up; in order to prevent memory leaks, this clean-up should be handled by the BOF. 

We have also added a new API that can be used by BOFs to get information on Beacon such as the Beacon address, sections to mask, heap records to mask, the mask, sleep mask address and sleep mask size information: 
BeaconInformation(BEACON_INFO * pBeaconInfo) 

This API is particularly useful for the Sleep Mask BOF as it uses it to populate the BEACON_INFO data structure, and this information can then be passed to the evasive sleep implementation to provide size information that is required to hide the sleep mask BOF. The sleep mask kit in the Arsenal kit has been updated with these changes.  

Examples of these changes have been added to the bof_template project in the public Cobalt Strike GitHub

Whilst not part of the main product release, another relevant update that we recently released is the ability to debug and test BOFs without having to spawn a Beacon. The BOF-VS template was created and a blog post authored during the current release cycle, and I’m including a reference here to highlight that work. Following user feedback and questions around how the BOF-VS is licensed and how they can publish their own work, we have decided to move the BOF-VS out of the Arsenal kit and publish it in the public Cobalt-Strike GitHub to address those issues. 

Sleep Mask Update 

The sleep mask processing has been updated to mask the sleep mask code that is patched into Beacon.  

System Call Updates 

An update has been made to the system call support that was added in the 4.8 release. Support for direct and indirect system calls has been added for the following functions:  

  • DuplicateHandle 
  • ReadProcessMemory 
  • WriteProcessMemory 

Product Security Updates

A change has been made to authorization files so that they are no longer backwards compatible with older versions of Cobalt Strike. This means that the authorization file generated when you update to or install the 4.9 release will not work with any 4.8 versions that you may also need to use. Whilst this change should not affect the majority of our users, we do know that some users test newer versions before using them in engagements and could be impacted by this change.  

Licensed users that need an old (pre-4.9) authorization file can generate one here: 

Backwards Compatibility

While we are on the subject of backwards compatibility of the authorization files, I feel that this is a good opportunity to reaffirm that Cobalt Strike itself is not backwards compatible with previous versions. This is a subject that comes up frequently after each new release.  

If you want to put Cobalt Strike into play in an existing engagement, you need to stand up a new team server and new Beacons need to be started. Ideally, users would wait until new engagements are about to be started before switching to the latest release rather than attempting this in the middle of an existing engagement. This is, of course, up to you – but please be aware of the issues that may arise when doing this.

Other Updates

We have made a few other, smaller changes in this release. 

The listeners listed in the Listener Chooser are now listed by name, instead of presented in a random order. 

The about box has been given an update so that it’s more responsive when it is expanded. 

We have added support for spawning processes under the impersonated user security context.  

A new Malleable C2 option, sleep, has been added to match the sleep command syntax added in the 4.8 release. The new setting accepts both seconds/jitter values or using the new d/h/m/s/j syntax. For example: 

sleep 20 25 
sleep 1d 3h 15m 30s 50j 

Note that if the sleep value is set, the existing sleeptime and jitter values cannot be used. The settings are mutually exclusive. 

Java Support

Whilst we haven’t made any changes in this release, we just wanted to give our users advanced notice that we are planning to update the minimum supported Java version from Java 8 to Java 11 in the next release. Hopefully, this won’t negatively impact any of our users but this should give you enough time to factor this in, if you need to make changes to your environment.  

To see a full list of what’s new in Cobalt Strike 4.9, please check out the release notes. Licensed users can run the update program to get the latest version, or download version 4.9 from the website. To purchase Cobalt Strike or learn more, please contact us