Cobalt Strike 4.10 is now available. This release introduces BeaconGate, the Postex Kit, and Sleepmask-VS. In addition, we have overhauled the Sleepmask API, refreshed the Jobs UI, added new BOF APIs, added support for hot swapping C2 hosts, and more. This has been a longer release cycle than in previous releases to allow us to make underlying architectural changes to support our longer-term ambitions.

Note: Cobalt Strike 4.10 introduces breaking changes to the update application. Licensed users will need to download version 4.10 from scratch. The existing 4.9 update application cannot be used to upgrade to version 4.10.

BeaconGate

Over the past few years there has been a dramatic increase in detection logic for anomalous API calls. For example, open-source projects such as syscall-detect, MalMemDetect, Hunt-Sleeping-Beacons, and pe-sieve all demonstrate the value of hunting for suspicious API calls from unbacked memory. Additionally, Elastic has pushed the defensive industry forward with their anomalous call stack detection logic that is a formidable challenge for modern red team operations.

Furthermore, prior to this release, it was difficult for operators to address the challenges outlined above with Cobalt Strike. For example, it was not possible to build on Beacon’s system call implementation and the only way to obtain granular control over Beacon’s API calls was via IAT hooking in a UDRL, which is complex and has a high barrier to entry.

As Cobalt Strike specialises in evasion through flexibility, this was a critical problem to solve and one of our key priorities for this release. Additionally, we wanted to provide a solution that avoided getting bogged down in complex implementation details and made it easy for users to apply custom TTPs to Beacon’s API calls. Our solution to these problems is BeaconGate.

At a high-level, the Sleepmask is conceptually similar to a Remote Procedure Call (RPC), albeit within the same process address space. For example, when Beacon sleeps, it will call into the Sleepmask BOF, mask, and sleep. Beacon here acts as the ‘client’ and the Sleepmask is the ‘server’ that executes the Sleep call on behalf of Beacon. In Cobalt Strike 4.10, we have taken this idea to its logical conclusion and the Sleepmask now supports the execution of arbitrary functions. Therefore, it is now possible to configure Beacon to forward its Windows API calls to be executed via the Sleepmask (aka BeaconGate).

This offers operators unprecedented control and flexibility;  we tell you what Beacon wants to call (and the arguments), and you can do what you want with it. Hence, BeaconGate gives users the ability to implement bleeding edge call stack spoofing TTPs and apply them universally to Beacon’s WinAPI calls. Additionally, as Beacon is now decoupled from its WinAPI calls, you can also mask Beacon while calling a potentially suspicious API. This is all implemented as a BOF, so you can configure different gates and completely change your TTPs by swapping out different Sleepmask BOFs.

By default, if you enable an API to be proxied via BeaconGate, Beacon will be masked while the API is executed. This means that out of the box, Beacon now has mask-and-call functionality. This is a useful mitigation against AV vendors who may trigger scans based on Kernel callbacks/ETW TI events.

BeaconGate can be configured by setting the new stage.beacon_gate Malleable C2 option, as demonstrated below:

stage {  
    beacon_gate {  
          All;  
    }  
}  

Valid values for this option are:  

  • Comms – Currently this is InternetOpenA and InternetConnectA (i.e., HTTP(S) WinInet Beacons only)
  • Core – This is the Windows API equivalents (i.e., VirtualAlloc) of Beacon’s existing system call API. See the BeaconGate documentation for the full list of supported functions.
  • Cleanup – Currently this supports proxying ExitThread via the Sleepmask. If this is enabled, then by default the Sleepmask will scrub/free Beacon from memory before exiting. Additionally, this provides an opportunity for operators to perform custom clean up before Beacon exits.
  • All – Comms + Core + Cleanup. 

It is also possible to forward specific functions from the supported set with the following syntax:

stage {  
    beacon_gate {
          VirtualAlloc;  
       VirtualAllocEx; 
       InternetConnectA; 
    }  
}  

As a note, some more intensive Beacon commands such as ps may spike CPU if you have the core set enabled. This is expected behaviour, as ps will call OpenProcess/CloseHandle multiple times while masking. If desired, you can disable BeaconGate at runtime via beacon_gate disable or alternatively disable the masking for specific functions in your own Sleepmask BOF. 

It is also important to point out that BeaconGate and Cobalt Strike’s existing syscall_method option are mutually exclusive; if you enable BeaconGate for an API, it will take precedence over system calls. However, you can enable BeaconGate for a specific API and use Beacon’s existing system call method for the rest. For example:

stage {  
    set syscall_method "Indirect"; 
    beacon_gate {  
         VirtualAlloc;  // Only VirtualAlloc is proxied via BeaconGate 
    }  
} 

Building On Top Of BeaconGate with Custom Sleepmask BOFs

In the previous section we covered BeaconGate’s default behaviour. However, the real power comes from building on top of BeaconGate. The potential here is unlimited; your own gate can implement novel system call techniques, spoof the call stack, fake the return address, utilize SilentMoonwalk, etc. – all while Beacon is masked (if desired). 

Beacon provides the higher level WinAPI (i.e., VirtualAlloc as opposed to NtAllocateVirtualMemory) to provide as much flexibility as possible. Hence, you can implement your own system call/gate for the NT function (e.g. something like RecycledGate) or unhook and call the original WinAPI function with a spoofed call stack etc. 
 
To demonstrate the possibilities, below is a quick PoC of BeaconGate implementing return address spoofing (while Beacon is masked) for Beacon’s InternetOpenA calls:

Fig 1. A screenshot showing BeaconGate implementing return address spoofing. A breakpoint has been triggered in windbg on WININET!InternetOpenA and the calling thread’s call stack is displayed (via the knf command). The call stack shows the calling function as WININET!UrlCacheFindFirstEntry, however this has been spoofed; the call is being proxied via the Sleepmask. Furthermore, the PowerShell terminal displays a YARA scan on the debugged process after this breakpoint has been hit. This reveals no hits, as Beacon is masked while the InternetOpenA call is made (prior to 4.10 Beacon would be exposed in memory at this point).

This example demonstrates that it is now possible to evade detection logic for anomalous InternetOpen/ConnectA calls, such as in MalMemDetect. However, the same technique could be applied to all the supported WinAPI functions. Additionally, the supported APIs cover the majority of detection use cases for anomalous call stacks from both ETW TI events and Kernel callbacks (with some exceptions, e.g. CreateProcess).

Sleepmask-VS

The Sleepmask is an essential part of Cobalt Strike’s evasion strategy. It started out as a tiny Position Independent Code (PIC) blob that could be stomped into Beacon. However, it has since grown into a fully featured BOF. While this change provided a huge amount of flexibility, its rapid growth has also made the Sleepmask quite complex, which means (in our experience) users often shy away from building on it.  

Furthermore, in dogfooding BeaconGate internally, we found it difficult to write custom Sleepmask BOFs with existing tooling. Therefore, one of the aims of this release was to lower the barrier to entry for writing custom Sleepmasks. 

As a result, we have updated our public BOF-VS template to support Sleepmask and BeaconGate functionality. This means our BOF-VS template is now a one-stop shop for writing all the various BOFs used by Cobalt Strike.  

Additionally, to provide a working Sleepmask BOF example we have also published Sleepmask-VS. This is a simple Sleepmask example that demonstrates how to use the BOF-VS template to write Sleepmask/BeaconGate BOFs. This repository will grow over time to contain a variety of different examples. In addition, it will be used as the accompanying Sleepmask BOF to the UDRL-VS loaders so that we can provide examples of how to use Cobalt Strike’s most important evasion tools together. 

Sleepmask-VS contains runMockedSleepMask() and runMockedBeaconGate() to make it easy to create custom Sleepmask/BeaconGate BOFs. These two functions are similar to the original BOF-VS runMocked() function, except they create a mock in-memory Beacon as well as some example heap memory. This allows users to step through their Sleepmask in the debugger and see the effects of their masking. These functions also allow users to provide their desired Malleable C2 settings to mimic the behaviour of Beacon’s default loader. 

An example call to runMockedSleepMask can be seen below:

int main(int argc, char* argv[]) { 
    bof::runMockedSleepMask(sleep_mask, 
        { 
            .allocator = bof::profile::Allocator::VirtualAlloc, 
            .obfuscate = bof::profile::Obfuscate::False, 
            .useRWX = bof::profile::UseRWX::False, 
            .module = "", 
        }, 
        { 
            .sleepTimeMs = 5000, 
            .runForever = true, 
        } 
    ); 
    return 0;

Sleepmask-VS also provides an example of how to use the runMockedBeaconGate function. This function replicates Beacon invoking the Sleepmask with a BeaconGate call and also passes in mocked Beacon/heap memory to be masked. This makes it easy for operators to start developing their own custom gates.  

For example, the sample code below demonstrates proxying a VirtualAlloc call through BeaconGate:

// Create a FUNCTION_CALL structure
FUNCTION_CALL functionCall = bof::mock::createFunctionCallStructure( 
    VirtualAlloc,   // Function Pointer 
    WinApi::VIRTUALALLOC, // Human readable WinApi enum 
    TRUE, // Mask Beacon? 
    4, // Number of Arguments (for VirtualAlloc) 
    GateArg(NULL),  // VirtualAlloc Arg1/Rcx 
    GateArg(0x1000), // VirtualAlloc Arg2 /Rdx 
    GateArg(MEM_RESERVE | MEM_COMMIT), // VirtualAlloc Arg3/R8 
    GateArg(PAGE_EXECUTE_READWRITE) // VirtualAlloc Arg4/R9 
); 

// Run BeaconGate 
bof::runMockedBeaconGate(sleep_mask, &functionCall, 
    { 
        .allocator = bof::profile::Allocator::VirtualAlloc, 
        .obfuscate = bof::profile::Obfuscate::False, 
        .useRWX = bof::profile::UseRWX::False, 
        .module = "", 
    }
); 

// Free the memory allocated by BeaconGate 
VirtualFree((LPVOID)functionCall.retValue, 0, MEM_RELEASE);

The FUNCTION_CALL structure contains all the information required to execute an “atomic” function call and is what is passed by Beacon to the Sleepmask as part of BeaconGate. The createFunctionCallStructure is a helper function which makes it easy to generate these structures for use in your own code. Lastly, the bof::runMockedBeaconGate function will call the Sleepmask entry point and pass your FUNCTION_CALL for it to be executed by BeaconGate. For more details on the exact API usage and function definitions, see Sleepmask-VS
 
There will be a further deep dive on BeaconGate post-release that will demonstrate how to get started developing your own custom TTPs and demonstrate a few different open-source gates. As a taster, the return address spoofing PoC demonstrated previously was developed using Sleepmask-VS. 

Additionally, we also identified that inline assembly was an important capability to port low-level techniques such as RecycledGate to BeaconGate. Hence, we will also discuss how to do this in the upcoming blog.  

It is possible to use ld with Sleepmask-VS to do this currently via combining two different object files but is not ideal (NB MSVC’s link.exe does not support this). Hence you will need to compile your assembly stub into a separate object file (i.e. via MASM/ml64.exe) and then manually combine it with the sleepmask.x64.o produced by Sleepmask-VS:

> ml64.exe /Fo asm_funcs.o /c asm_funcs.asm 
Microsoft (R) Macro Assembler (x64) Version 11.00.50727.1 

Copyright (C) Microsoft Corporation.  All rights reserved. 

Assembling: asm_funcs.asm 
[ ... ] 

(In WSL/Linux) 
> ld --oformat pe-x86-64 -r sleepmask.x64.o asm_funcs.o -o sleepmask.x64.o

Lastly, to enable operators to get the most out of BeaconGate, we have bumped the max Sleepmask BOF size.

Beacon Object File Updates

We have also made a number of changes to help users get more out of BOFs in this release. 

We have expanded the BOF API to expose Beacon’s system call functionality to BOFs. The new APIs take the form of Beacon<WinAPI>, i.e. BeaconVirtualAlloc. We added new APIs in order to give operators as much flexibility as possible. Hence, users can ‘opt in’ to using Beacon’s sys call API if desired, as opposed to transparently linking and not having a choice. 

As an example, the code below will route the VirtualAlloc call through Beacon’s system call code:

void go(char* args, int len) {  
    PVOID pMemoryBuffer = NULL;  
    pMemoryBuffer = BeaconVirtualAlloc(NULL, 8, MEM_COMMIT, PAGE_READWRITE);  
} 

The sys call method used by Beacon will be the option configured via the Malleable C2 syscall_method option or via the runtime syscall-method command. 

Furthermore, these new BOF APIs are also supported by BeaconGate. Hence, if you have your own custom gate configured, you can proxy WinAPI calls from a BOF to be executed by your custom gate. This gives operators complete control over Beacon’s API usage/footprint and reduces BOF code bloat.
 
Further details on the new APIs can be found in the documentation here. Additionally, an example BOF can be found in the bof_template in the public Cobalt Strike GitHub repository, which demonstrates a trivial example of using the new APIs to allocate and free memory. 
 
A new Beacon API, BeaconGetSyscallInformation, has also been added, which means you can now implement any syscall resolving technique you want in your loader, pass the resolved syscall info to Beacon via Beacon User Data(BUD), and then retrieve it from within a BOF. This is intended to reduce the bloat from within BOFs of having to repeatedly calculate syscall info yourself. For more detailed information on the API see the documentation here.  
 
As a note, the bud-loader in UDRL-VS demonstrates how to pass resolved syscall info to Beacon via BUD and our public BOF-VS template contains a mocked BeaconGateSyscallInformation API, making it easy to integrate into your own BOFs. 
 
Lastly, the BOF API limit has been expanded to 128 ( 😉 @s4ntiago_p ).

Sleepmask Redux

From talking to customers, we are aware of confusion around the interoperability between the Sleepmask and UDRLs. The confusion stems from the fact that transformations set in the Malleable C2 profile are not applied to Beacons generated via the BEACON_RDLL_GENERATE hook. In contrast, the Sleepmask settings are statically calculated from the Malleable C2 profile and Beacon DLL irrespective of whether you’re using a UDRL.

For example, stage.obfuscate can be used to obfuscate parts of the Beacon DLL and it also instructs the default loader not to copy the PE header into memory as part of the reflective loading process. However, this does not apply to Beacons with UDRLs. This is expected behaviour, as it puts the developer in the driving seat (i.e., the UDRL must know how Beacon has been obfuscated in order to reverse it). However, the Sleepmask will use stage.obfuscate to calculate what sections it needs to mask, and hence will assume there is no PE header present. This is an obvious source of issues if a UDRL does not honor the Malleable C2 profile settings.

The introduction of BeaconGate meant we had to make some breaking changes to the Sleepmask API and this also provided us with an opportunity to address this issue. In CS 4.10, we have expanded Beacon User Data to include a new ALLOCATED_MEMORY structure. This structure can be used to pass information to Beacon about (dynamic) memory allocated by the reflective loader. For example, Beacon’s location in memory and the address of each loaded section. This design means that Beacon can now pass the Sleepmask accurate section information, at run time, which greatly simplifies Sleepmask design. This feature also opens up a lot of possibilities, as any memory allocated by the loader can now be automatically masked by the Sleepmask.  

For specific implementation details, the bud-loader and the obfuscation-loader in UDRL-VS contain comprehensive examples to demonstrate how to use ALLOCATED_MEMORY in a UDRL. The design was heavily influenced by Microsoft’s own abstractions around Virtual Memory and we are planning to release a deep dive on how to get the most out of this feature in the coming months. 

It is important to note that configuring the Sleepmask via the ALLOCATED_MEMORY / BEACON_USER_DATA structures is the intended workflow as of the 4.10 release. Beacon will still try to mask based on a best effort basis if you do not pass this information (i.e., statically), but it may not work as expected. However, in future releases we plan to remove backwards compatibility. This means that UDRLs must pass allocated memory to Beacon in order to use the Sleepmask.

User Defined BOF Memory

We have also added support to the ALLOCATED_MEMORY structure for passing Beacon user defined memory which can be used for BOF and Sleepmask execution. Therefore, if you want full control over how the memory used for BOFs is allocated, you can employ your own custom allocation technique in a UDRL and pass this information to Beacon. This now enables operators to employ techniques such as module stomping when loading/executing BOFs. The bud-loader in UDRL-VS contains an example of how to pass user defined memory to Beacon for use with inline-execute and the Sleepmask.

Postex Kit

Another new addition in 4.10 is the Postex kit. The Postex kit opens up Beacon’s job architecture to allow operators to develop their own post-ex DLLs for interoperability with Beacon. Hence, if you need to quickly PoC a custom keylogger/session monitor/TGT monitor etc., you can use the Postex kit to develop a DLL which can plug seamlessly into Beacon’s existing jobs architecture. Furthermore, DLLs generally are simpler to develop and unit test for complex/long running tasks and suffer from none of the pain points and limitations which can make BOF development difficult. 

It is important to highlight that the Postex kit also supports post-ex UDRLs (introduced in 4.9), via the POSTEX_RDLL_GENERATE Aggressor hook, and Process Injection hooks, via the PROCESS_INJECTION_* Aggressor hooks. This gives operators full control over the whole post-ex attack chain, in terms of custom capabilities and how they are injected/loaded into memory. The process injection kit is still (in our experience) under utilised and it is worth checking out this blog for more information on how to configure it. 
 
The Postex kit itself is primarily intended to serve as a template for development. It is a Visual Studio solution that can be found in the Arsenal kit and makes it easy to develop custom long running post-ex DLLs that return data back to Beacon over a named pipe. It includes a library of functions which provide an abstraction over the job architecture allowing operators to focus purely on developing custom tooling. As a note, the Postex kit has been designed in a way that makes it possible to provide support for alternate methods of communication in future releases (i.e. not just named pipes). 
 
To support the Postex kit, a new execute-dll command has been added to the Beacon console. This will take a custom post-ex DLL provided by the operator, prepend a post-ex loader to it, and execute it as a new job. This job can be seen via the normal Cobalt Strike jobs output and killed via the jobkill command.  

Additionally, the execute-dll command also supports passing arguments. These are automatically patched into a separate memory allocation and can be accessed from within the post-ex DLL via the postexData->UserArgumentInfo.Buffer (See the Postex kit example DLL for more information). 
 
However, one of the most powerful features of Cobalt Strike is its scripting language, Aggressor Script, which provides a huge amount of flexibility to operators. Hence, we have also added a new Aggressor Script function beacon_execute_postex_job.  

This works in a similar way to execute-dll except it supports passing BOF style arguments (i.e. via Aggressor Script’s bof_pack function) to your custom post-ex DLL. This enables operators to use the familiar Beacon Data Parser and Beacon Data Format APIs from within their post-ex DLLs. A trivial example of beacon_execute_postex_job is shown below:

        $argument_string = "example argument string"; 
        $packed_arguments = bof_pack($beacon_id, "iz", 4444, $argument_string); 
 
        # example: run the postex task 
        beacon_execute_postex_job($beacon_id, $pid, $postex_dll, $packed_arguments, $null);

From the post-ex DLL, the packed arguments can then be parsed via the standard BeaconDataParse/Extract APIs. As ever with Aggressor Script, there is huge scope for customisation here. For example, if desired, you could also stomp arguments directly into the post-ex DLL and retrieve them yourself. 
 
Furthermore, we have also added another Aggressor Function, bjob_send_data. This means that operators can now send arbitrary data to a custom post-ex job via a named pipe. An example demonstrating this can be seen below:

        # String to send to the post-ex dll         
        $pipe_data= "example pipe data string"; 
 
        # Send the string to target post-ex job over named pipe       
        bjob_send_data($bid, $jid, $pipe_data);    

This provides a huge amount of flexibility in your tooling. As a quick example, below is a screenshot of a custom inject-assembly PoC that we developed internally to dogfood the Postex kit:

Fig 2. A screenshot showing a custom inject-assembly PoC demonstrating use of the Postex kit. This example post-ex DLL waits for arguments to be passed via a named pipe and repeatedly executes the DotNetHelloWorld.exe assembly with the passed arguments. 

The inject-assembly command above uses beacon_execute_postex_job under the hood to inject the PoC post-ex DLL into a remote process along with the user provided .NET assembly. The post-ex DLL then sits listening on a named pipe waiting for the user to send some arguments via bjob_send_data. In the example above we’ve run the target assembly twice using a different set of arguments.  

The Postex kit also contains an example post-ex DLL (which must be used in conjunction with the postex-example.cna) to get up and running to start developing your own tooling. This example post-ex DLL is primarily intended to demonstrate different aspects of the Postex kit in one place. Additionally, the Postex kit documentation can be found here. As with other new 4.10 features, we plan to release a deep dive on the Postex kit (and the custom inject-assembly DLL demonstrated above) in the upcoming months. 

Callbacks Update

Callbacks were introduced in the 4.9 release and have been extended in 4.10 to provide a straightforward method for users to interact with the Postex kit. 
 
For example, you can also call beacon_execute_postex_job and provide a custom callback function as the last argument, which will be invoked each time the job checks in. The custom callback is passed a new %infomap hash map, which contains various information, such as the status of the job (i.e. has the job just been registered, completed, is it sending output etc.) and its job id. The key point is that the callback has access to the job id (once the job has been registered) which can then be used to send further data via bjob_send_data().

A quick example of this functionality is shown in the cna script snippet below, which demonstrates sending further data to a custom post-ex DLL via a callback once it has been registered:

$pipe_data= "example pipe data string"; 
 
# define custom callback function 
        $callback = lambda({ 
            local('$bid $data $result %info $type'); 
            this('$jid'); 

            # get all arguments passed to lambda 
            ($bid, $result, %info) = @_; 
 
            # check the status/type of the job 
            $type = %info["type"]; 
 
            # if the job is registered, send data via the pipe 
            if ($type eq 'job_registered') { 
                $jid = %info['jid']; 

                # send the pipe data string to the DLL 
                bjob_send_data($bid, $jid, $pipe_data);     
            } 

            # print output to the console 
            else if ($type eq 'output') { 
                bjoblog($1, $jid, "Received output:\n" . $result); 
            }  

        }, $pipe_data=> $pipe_data; 

        # run the postex task... 
        beacon_execute_postex_job($beacon_id, $pid, $postex_dll, $packed_arguments, $callback);

Job Browser and Console

To enable you to get the most out of the Postex kit we have given Cobalt Strike’s job UI an update with the introduction of the new job browser and job console. This has also been a common pain point for customers, as prior to 4.10 it was difficult to map job output to a specific job id. 

The job browser is a new dialog that enables a user to work with jobs being run by one or more Beacons. It can be opened by selecting one (or multiple) Beacons, right-clicking, and selecting the Jobs option from the popup menu, or by selecting View -> All Jobs from the main menu.  

The job browser shows a complete list of every job tasked to the given Beacon(s) and shows various information such as the Job ID (JID), its status (i.e., if it has completed or still running), its description, and start and stop times. An example of the job browser is shown below:

Fig 3. A screenshot showing the new job browser UI.

The job console is another new dialog that allows a user to work with the output of a specific job. It is invoked by right-clicking a target job in the job browser and selecting open from the popup menu. An example of the job console for a portscan job is shown below: 

Fig 4. A screenshot showing the new job console UI for a portscan.

It is also possible to hide the output from selected jobs from the Beacon console. The output is then redirected to only appear in the job console window. This improves the user experience for long running post-ex jobs, such as SharpHound, as it means the output is all in one place and allows users to continue to operate in the Beacon console window. Additionally, if you need to revisit the output of a specific job you can now open the job console as opposed to having to trawl up through the Beacon console history.

Host Rotation Updates

Cobalt Strike’s host rotation was introduced in 4.3 to provide operators with greater control over how the HTTP/S and DNS Beacons cycle through hosts. While this offered additional flexibility over C2 comms, host rotation suffered from two main problems:  

  • Unresponsive hosts were included in the rotation strategy regardless of whether they were responsive. Hence, if one out of three hosts were failing, 1/3 check-ins would repeatedly fail.
  • There was no way to update host information on an active Beacon in order to change or disable failing hosts.

In Cobalt Strike 4.10, we have made a number of improvements to host rotation to address these issues:

  • Beacon will now automatically disable (“hold”) hosts that have failed, resulting in a far more reliable connection.
  • A new Beacon command, beacon_config, and its corresponding Aggressor Script function, bbeacon_config, have been added to make it possible to query and update the host information for an active Beacon. Hence, it is now possible to hot swap C2 hosts (via adding a new host or updating an existing one).
  • Operators can enable notifications for failed connections making it much easier to debug host rotation issues.

As an example, we can query an active Beacon’s current host information via the new beacon config host info command, as demonstrated below:

Fig 5. A screenshot showing the new beacon_config command in action. The output shows that only one host (example.yyy) has currently been configured for RoundRobin host rotation on the active Beacon.

The screenshot below shows a new host (example.zzz) being added at run time:

Fig 6. A screenshot showing hot swappable C2 in action. A new host (example.zzz) has been dynamically added, which will now be used as part of the existing RoundRobin host rotation strategy. 

Wireshark shows that Beacon immediately starts using the new host to check-in:

Fig 7. A TCP Stream in Wireshark demonstrating Beacon using the dynamically added host (example.zzz) to check-in. 

As a note, the one restriction for adding hot swappable C2 hosts is that the URI must have previously been defined in the Malleable C2 profile

Lastly, some web/proxy servers (when blocking requests) return a 200 (OK) status without any additional data in response to Beacon check-ins. Beacon assumes this is a valid “nothing to do” response and hence will not trigger a failover rotation to the next host. To address this issue, users are now able to customise the format of the “nothing to do” task so Beacon can determine whether a given response is valid. This can be enabled via the new Malleable C2 profile options, data_required and data_required_length.  

For more information on all of these host rotation updates, see the documentation here.

Java Support Updated To Java 11

As referenced in the Cobalt Strike 4.9 release blog post, we have changed the minimum supported version of Java from Java 8 to Java 11. If you attempt to run the client using an older version of Java, you will see the following error:

Fig 8. A screenshot displaying the error message presented when the Cobalt Strike 4.10 client is run with an older version of Java.

To avoid any issues, please make sure that the version of Java in your environment is at least Java 11 before downloading and running Cobalt Strike. For more guidance, see the Cobalt Strike installation guide.

Product Security Updates

Product security controls have been updated as part of the 4.10 release. In particular, the Linux package now splits the client and server out into separate packages, with each requiring a specific authorization file. This has resulted in a breaking change to the way Cobalt Strike updates, which you may need to account for in any bespoke deployment scripts. 

Additionally, Fortra has partnered with Europol, the UK National Crime Agency, and several other private partners to protect the legitimate use of Cobalt Strike. In June, 593 IP addresses were taken down to disable stolen, unauthorized versions of Cobalt Strike. Fortra and law enforcement will continue to monitor and carry out similar actions as needed. You can read more about the action here.

Additional Updates

In addition, this release also includes updates to System Calls, External C2, as well as numerous quality-of-life (QoL) changes. These QoL updates include: 

  • Improvements to tab completion (including support for custom commands, shift + tab functionality, and case insensitivity).
  • UI Improvements (including better word wrapping for dialogs and a preference to allow users to specify if they want Cobalt Strike opened in a maximized window).
  • Ability to specify the time zone and timestamp format used for logging (configurable via Teamserver.prop). 

To see a full list of what’s new in Cobalt Strike 4.10, please check out the release notes.  

Licensed users will need to download version 4.10 from scratch. The existing 4.9 update application cannot be used to upgrade to version 4.10. 

To purchase Cobalt Strike or learn more, please contact us.