Cobalt Strike is one of the most popular command-and-control frameworks, favoured by red teams and threat actors alike. In this blog post we will discuss strategies that can be used by defenders and threat hunters to detect Cobalt Strike across different configurations and across the network, using the techniques outlined in Part 1 of this series. All analysis is performed on Cobalt Strike 4.6.1; the latest at the time of writing.
The Cobalt Strike beacon is highly malleable and as such some indicators may vary depending on the malleable profile options selected.
Hunting for Cobalt Strike signatures in memory has been fruitful for threat hunters in the past, with prior comprehensive write ups being provided by Elastic. However, since then much work has been done by HelpSystems, with Cobalt Strike 4.4 introducing the Sleep Mask Kit.
Cobalt Strike provides the following possible configuration options for it’s obfuscate and sleep strategies:
As an example, Cobalt Strike can be detected with the following Yara rule when using the Sleep Mask Kit with the userwx option set to false:
rule CobaltStrike_sleepmask {
meta:
description = "Static bytes in Cobalt Strike 4.5 sleep mask function that are not obfuscated"
author = "CodeX"
date = "2022-07-04"
strings:
$sleep_mask = {48 8B C4 48 89 58 08 48 89 68 10 48 89 70 18 48 89 78 20 45 33 DB 45 33 D2 33 FF 33 F6 48 8B E9 BB 03 00 00 00 85 D2 0F 84 81 00 00 00 0F B6 45 }
condition:
$sleep_mask
}
Running this Yara rule against an injected beacon will show detection of the signature:
Enabling the userwx will set the page permissions to EXECUTE_READWRITE but means the beacon is now obfuscating its .text section:
Cobalt Strike beacons will typically be operating from a page with either RX or RWX page permissions, depending on the value of the “userwx” configuration option of the malleable profile and without module stomping, will be backed by unmapped memory.
This is a clear indicator that makes spotting the beacon relatively trivial in memory:
With care for avoiding JIT’d assemblies, it is possible to scan for these memory regions, searching for pages with PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_READ page permissions and the MEM_COMMIT flag. When used with other indicators, this may prove valuable for identifying beaconing activity. We built this check in to BeaconHunter when the -p flag is used:
When injected in to memory, Cobalt Strike will occupy a single thread; the beacon is synchronous. By default, the thread that the beacon operates within is highly suspicious and has a number of indicators associated with it.
Examining the thread of a Cobalt Strike beacon in Process Hacker might look something like this:
In the above screenshot alone, we can see a number of indicators that make the thread look highly suspicious:
Considering all these indicators in parallel, a threat hunter is able to determine with high confidence that malicious activity is originating from the thread. BeaconHunter will pluck out these threads as being suspicious due to these indicators:
It should also be noted that Cobalt Strike introduced stack spoofing to the arsenal kit in June ‘21. However, the call stack spoofing was only determined to apply to exe/dll artifacts generated through the artifact kit as opposed to the beacon injected through shellcode in an injected thread. As such, they are unlikely to pose effective in masking the beacon in memory.
Cursory analysis suggests that the use of fibers is rare, therefore these can be trivially hunted for by analysing the call stack for a start address of ntdll.dll!RtlUserFiberStart and when combined with other indicators, may provide a starting point for hunting Cobalt artifacts:
Cobalt Strike supports module stomping use the “set module_x64” and “set module_x86” malleable options. Configuring these options cause beacon to be backed by a module on disk, providing some OpSec benefits over operating from virtual memory.
However, the implementation of the Cobalt Strike module stomping technique does leave some indicators of compromise behind within the process’ PEB. Aside from comparing the in memory module with the copy on disk or removal of the shared bit, the actual technique for implementation allows defenders to create a highly accurate detection of module stomping. This detection technique has already been documented by @_winterknife_ and @ilove2pwn_.
Summarising the approach used by Cobalt Strike for module stomping, it first loads the sacrificial DLL using a call to LoadLibraryExA(moduleName, NULL, DONT_RESOLVE_DLL_REFERENCES):
This instructs the loader to not execute the DLLs entry point and avoids processing the DLL’s import table to load dependencies (which is preferred as its sacrificial).
However, the side effect of this approach is that certain attributes inside the LDR_DATA_TABLE_ENTRY structure within the PEB are left in a rare configuration; specifically the EntryPoint attribute will be set to NULL and the ImageDLL bit false.
Walking the PEB and parsing this structure can for the combination of these attributes in this configuration can provide a high confidence indicator:
The Cobalt Strike C2 server is based on NanoHttpd, this is a lightweight Java HTTP server and has undergone a small number of alterations to align it with the Cobalt Strike use case. Prior work has been done by FoxIT on identifying Cobalt Strike team servers in the wild; however, the fingerprint of the superfluous white space character has long since been patched. At the time, the FoxIT research showed that there were likely many more Cobalt Strike C2 servers in the wild than NanoHttpd servers.
A cursory analysis of the Cobalt Strike C2 server revealed a number of further methods for fingerprinting the C2.
The first one involves the use of the Range HTTP header, where sending a request with an invalid integer will will cause no response to be returned by the server:
Taking a closer look at the team server, we can see the reason for this. An unhandled exception has occurred:
Taking a closer look at the C2 server source code (src/main/java/cloudstrike/WebServer.java), we can quickly find the reason for this; the server attempts to convert the string to an integer but the value is invalid and no exception handling is in place:
A second technique for fingerprinting the Cobalt Strike C2 server also exists within the Range HTTP header. Providing a range that the server cannot satisfy (e.g. 1-0), will lead to a “Range Not Satisfiable” error. While this error will also occur in NanoHTTPd servers, we can imply that it is Cobalt Strike through the erroneous Server header; that is servers such as IIS, Apache and Nginx do not return this error:
Analysing the C2 server source code further, we can easily identify other responses that can lead to fingerprinting, such as the following found in src/main/java/cloudstrike/NanoHTTPD.java:
As we can see above, when an invalid URL encoded byte is submitted in the URI, the C2 server will return a finger printable response.
Using this logic, we can build a Nuclei template to scan for team servers:
id: csteamsever-badencoding
info:
name: Cobalt Strike Team Servers
author: Dominic Chell
severity: info
description: description
reference:
- https://
tags: tags
requests:
- raw:
- |-
GET /%0 HTTP1.1
Host: {{Hostname}}
unsafe: true
matchers-condition: and
matchers:
- type: word
part: body
words:
- 'BAD REQUEST: Bad percent-encoding.'
- type: status
status:
- 400
With an example match as follows:
In conclusion, we’ve outlined a number of techniques that can be used by defenders to identify malicious activity from Cobalt Strike beacons in the wild, including indicators in memory, within threads and across the network.
References:
This blog post was written by Dominic Chell.
For further information on finding Cobalt Strike in your network, contact us for more details on our Threat Hunting Services.