Cobalt Strike 4.8 is now available. This release sees support for system calls, options to specify payload guardrails, a new token store, and more.  

We had originally planned to get this release out late in 2022 but progress was stymied due to the 4.7.1 and 4.7.2 patch releases that we had to put out to fix vulnerabilities that were reported in the 4.7 release. We spent a few development cycles performing a security review of the code and working on some technical debt, and then it was the holiday season. It’s here now though, and better late than never!  

Before getting into the details of this release, I just wanted to mention that you should now start to see much more content from us to supplement main product releases. William Burgess recently released his first blog post since joining the Cobalt Strike team and he will be playing a key role in providing technical guidance on the future direction of the product. We have more blog posts and tooling coming over the next few weeks and months, starting with a series on UDRL development (the first of which should drop next week). Coming later in the year are some huge changes to Cobalt Strike itself. More details on that will come in a follow-up blog post soon. We know that our users are struggling with evasion and have reported other pain points. As I mentioned in my roadmap update last year, we have been aggressively building out our R&D team and while it’s taken a while to do that and get all of our ducks in a row, you’ll now really start to see the benefits of those behind-the-scenes changes. Now, back to the 4.8 release. 

System Calls Support

This release sees the addition of support for direct and indirect system calls. We have added support for a number of system calls, specifically:  

  • CloseHandle
  • CreateFileMapping
  • CreateRemoteThread
  • CreateThread
  • GetThreadContext
  • MapViewOfFile
  • OpenProcess
  • OpenThread
  • ResumeThread
  • SetThreadContext
  • UnmapViewOfFile
  • VirtualAlloc
  • VirtualAllocEx
  • VirtualFree
  • VirtualProtect
  • VirtualProtectEx
  • VirtualQuery

The stageless Beacon payload generation dialog has been updated to allow you to specify the system call method to be used at execution time. The available options are:  

None: Use the standard Windows API function
Direct: Use the Nt* version of the function
Indirect: Jump to the appropriate instruction within the Nt* version of the function

It is important to note that there are some commands and workflows that inject or spawn a new Beacon that do not allow you to set the initial system call method. Those commands/workflows are:

  • elevate 
  • inject 
  • jump 
  • spawn 
  • spawnas 
  • spawnu 
  • teamserver responding to a stageless payload request 
  • teamserver responding to an External C2 payload request 

The stage.syscall_method in the MalleableC2 profile controls the method used at execution time, and you can use the syscall-method [method] command to modify the method that will be used for subsequent commands. Additionally, syscall-method without any arguments will query and return the current method.  

System call support is something that we intend to continue to update and enhance in future releases. Your feedback on this is welcomed.  

Generate Payloads With Built-In Guardrails 

Support has been added for payload guardrails, which can be set at the listener level and then, if required, overridden when generating a payload.  

Guardrails can be set based on the following criteria: 

  • IP address: This can be a single IP address or a range using a wildcard to replace the rightmost octet(s). For example, 123.123.123.123, 123.123.123.*, 123.123.*.* and 123.*.*.* are all valid inputs. 123.*.123.* is not. 
  • Username: This can be a specific user name, or you can prefix/suffix a wildcard (i.e. *user or user*). The username field is case insensitive.  
  • Server name: Again, this can be a specific server name, or you can prefix/suffix a wildcard (i.e. *server or server*). The server name field is case insensitive. 
  • Domain: As with username and server name, the domain field can either be a specific domain or you can prefix/suffix a wildcard (i.e. *domain or domain*). The domain name field is also case insensitive.

The listener dialog has a new “Guardrails” option at the bottom of the screen that allows you to set and update guardrails for that listener.  

When generating a payload, the guardrails value from the associated listener is used as a default value.  

You can use the default values or override them to set specific values for the payload being created. Setting specific values here does not change the default values set at the listener level.

Multi-Byte Support For Obfuscating Beacon’s Reflective DLL’s Import Table

We have made a change to the obfuscation routine used for the stage.obfuscate MalleableC2 option which relates to how Beacon’s Reflective DLL’s import table is obfuscated.

Part of this process involved moving from a fixed, single byte XOR key to a randomly generated multi-byte XOR key. The single byte XOR mask was easily signatured and caught by tools such as YARA. Moving to a randomly generated multi-byte XOR key should help address those issues. 

Sleep Mask Updates

A number of updates have been made to the Sleep Mask. The main change is that the Sleep Mask size limit has been increased from 8192 to 16384 bytes. Other changes include: 

  • Support for the use of system calls with the MASK_TEXT_SECTION capability
  • The addition of define tags for the Windows API functions to remove the need for LIBRARY$Function syntax
  • The implementation of evasive sleep via stack spoofing (x64 only). Related changes include the addition of a bypass for Control Flow Guard (CFG), as well as the addition of a helper utility (getFunctionOffset)

Token Store

One change that we’ve had on the backlog for a while was the addition of a token store, to facilitate the hot swapping of access tokens. Windows tokens are process specific; hence each Beacon has its own token store with its own tokens. Please be aware that those tokens are therefore only available to be used by that specific Beacon.

The token store is based around the new token-store command. The command supports a number of options that perform specific functions (all supported by tab completion). Available functions are as follows:

token-store steal [pid,…] <OpenProcessToken access mask>
This steals the token(s) from the specificed PID(s). Use commas to separate each PID. This command will steal the token and put it into the token store, but will not impersonate straight away. The steal-and-use command should be used for that purpose (although it should be noted that steal-and-use only supports a single PID). This command supports the optional OpenProcessToken access mask like the existing steal_token command. 

token-store use [id]
This tasks Beacon to use the token with the specified ID from the token store.  

token-store steal-and-use [pid] <OpenProcessToken access mask>
This allows you to steal the token from the specified PID, add it to the token store and immediately use it. This command supports the optional OpenProcessToken access mask like the existing steal_token command. 

token-store show 
This displays the tokens in the token store. 

token-store remove [id,…]
This allows you to remove the token(s) corresponding to the specific ID(s) from the token store. Use commas to specify multiple IDs.  

token-store remove-all 
This removes all tokens from the token store. 

One additional point is that tokens can also be stolen via the process list in the GUI. Steal a token in the usual way (i.e. Explore -> Process List, select one or more PIDs and click on Steal Token) and then make sure the “store token in the token store” checkbox is checked before stealing the token. You are also able to select multiple Beacons at once before opening the process list.  

Finally, we have also implemented corresponding aggressor functions to the options described above, i.e.:  

  • btoken_store_remove 
  • btoken_store_show 
  • btoken_store_steal_and_use 
  • btoken_store_steal 
  • btoken_store_remove_all 
  • btoken_store_use 

ETW Blinding 

This release also sees the addition of support for ETW blinding via patching for the execute-assembly and powerpick commands. While there is limited support in this release, this is something that we will look to build on in future releases.  

The execute-assembly and powerpick commands now have an optional PATCHES parameter, specified as follows: 

execute-assembly “[PATCHES: [patch-rule] [patch-rule] [patch-rule] [patch-rule]]” [/path/to/file.exe] [arguments] 

and  

powerpick “[PATCHES: [patch-rule] [patch-rule] [patch-rule] [patch-rule]]” [commandlet] [arguments] 

The optional PATCHES parameter can modify functions in memory for the process. Up to four patch-rule rules can be specified (delimited by spaces), with each patch-rule being made up of a library, function, offset and hex patch value, for example: [library],[function],[offset],[hex-patch-value], where:

  • library can be 1 – 260 characters  
  • function can be 1-256 characters 
  • offset is the offset from the start of the executable function and can be 0 – 65535 
  • hex-patch-value can be 2 – 200 hex characters (0 – 9, A – F) and the length must be and even number (hex pairs)

Further information on the patch-rule syntax can be found in the documentation.  

Sync Data At Teamserver Startup 

There has been a long-standing issue within Cobalt Strike whereby any data retrieved from a target (for example, screenshots, keylog data etc.) is unavailable in the client after the teamserver is restarted. This issue has been addressed, and data that was previously “lost” to the UI is now persisted between restarts.  

We have also added a new script, clearteamserverdata, that can be used to delete all data after an engagement has completed. 

Change How License Expiration Date Is Processed 

A key update related to product security is a change to how the license expiration date is processed. Previously, the expiration date was checked at teamserver startup and if it was valid, the teamserver was able to start. No further checks were performed and the teamserver was able to run until shut down by the operator.

We have tightened this processing up so that the license expiration date is checked on a daily basis. The reasoning behind the previous behaviour was that it wasn’t always convenient or logistically possible to run an update in the middle of an engagement (to grab a new authorization file with an updated expiration date). Nor was it convenient under those circumstances to restart the teamserver following an update. We have added mitigations for those scenarios in the new processing, meaning that there will be no impact on operations while refreshing your authorization file. If you need to update your license while an engagement is running, please consult the documentation for information on how you can do this. We do recommend, however, that you consider your license expiration date before starting a new engagement and avoid this situation occurring, if at all possible.

To make sure that you don’t get caught out by an expiring license, we have added a couple of new banners to the client UI.  

Starting 45 days before your license is due to expire, a warning banner will appear in the client, informing you that your license is expiring and when it is due to expire. This warning message can be dismissed but will reappear each day when the expiration date is checked. This should provide sufficient time to renew your license key if this has not already been taken care of:

If you fail to renew your license before it expires, you will have a 14-day grace period in which to do so. For the duration of that grace period, you will see an error banner that cannot be dismissed:

After the grace period has ended, if your license has not been renewed and the authorization file refreshed, the teamserver will shut down and you will be unable to restart it until you have renewed your license.  

Quality Of Life Changes 

In addition to the features already mentioned, we have also added a number of smaller changes, mainly requested by our users, as follows:

Added Flexibility To Specifying Sleep Time

A small change requested by a user was to make the sleep time easier to set. Rather than only being able to specify the sleep time in seconds, you can now also specify days, hours and minutes by suffixing those values with “d”, “h” and “m” respectively.  

A usage example is sleep 2d 13h 45m 8s 30j which translates to “sleep for 2 days, 13 hours, 45 minutes, 8 seconds with 30% jitter”. 

We have also added a new aggressor function, bsleepu that works in the same way.  

Display Current Token In The UI

Another user request was to display the current token in the client UI. This gelled nicely with the new token store and you are now able to see the current token the table view and status bar in brackets alongside the user name.  

Related to this change, we also addressed the issue whereby make_token would report the wrong user name (i.e. the user name of the current Beacon process) due to a Windows API limitation. This has been addressed and the correct username appears in brackets.   

Copy/Paste From The Beacon Output Pane 

We have added support for copying and pasting commands from the Beacon output pane.  

CTRL+C copies and CTRL+X cuts the selected text from either the output pane or the command line. Text can only be selected in the output pane or command line, not both. CTRL+V pastes text from the clipboard onto the command line. This works for any console window (for example, the Beacon console, SSH console, script console, event log and web log).  

Chain Multiple Commands In A Single Mimikatz Call 

The mimikatz command has been updated to support chaining multiple commands in a single operation. This also applies to the bmimikatz and bmimikazt_small aggressor functions (although bmimikatz_small is limited to lsadump::dcsync, sekurlsa::logonpasswords and sekurlsa::pth).  

Commands can be chained by adding a semicolon as a delimiter between commands, for example:
mimikatz standard::coffee;standard::coffee  

Note that the semicolon can still be escaped if it is required in a command (i.e. “\;”). Also, the command length is limited to 511 characters to ensure that the final character in the string is EOS (\0).  

Specify Exit Function On Stageless Windows Executable Dialog

A change was made in the 4.7 release to allow you to specify the exit option (either “thread” or “process”) on the Stageless Payload Generator dialog.  A similar change has now been made to the Stageless Windows Executable dialog that also allows you to specify the exit option (“thread” or “process”).  

Arsenal Kit Checksum

One final change to mention, again requested by a user, was to add a checksum for the Arsenal Kit. This has been done and it can be found at https://download.cobaltstrike.com/scripts .

To see a full list of what’s new in Cobalt Strike 4.8, please check out the release notes. While licensed users can run the update program to get the latest version, we recommend that you download version 4.8 from scratch from the website to get the new update application. This is because the TLS certificates on download.cobaltstrike.com and verify.cobaltstrike.com will be updated soon, and the existing updater will report errors if it isn’t updated. To purchase Cobalt Strike or learn more, please contact us

Read the latest release blog: