Cobalt Strike (S0154) is a commercial penetration testing platform which is used by many red teams and, unfortunately, also by many criminal threat actors. In this post I summarise the findings from a SANS Digital Forensics and Incident Response keynote by Chad Tilbury : Cobalt Strike Threat Hunting. The YouTube video provides much more details but below you can find those findings that were relevant for me during an IR case.
This post includes references to commands of the Volatility memory forensics platform but you can use any platform of your choice. Some of the logs sources that are valuable during IR such as network traffic (detect beaconing, traffic frequency), PowerShell logs and Sysmon logs were unfortunately not available.
Look at running processes and the process tree via pstree. Two anomalies to look for are:
- PowerShell spawning itself;
- PowerShell spawning (multiple) rundll32.exe processes.
Rundll32.exe is spawned as a sacrificial process in which Cobalt Strike injects its malicious code. By default, Cobalt Strike will spawn rundll32.exe. This is customisable and can as well be svchost.exe or any other process. The take-away is to look for multiple, similar, processes all spawned from PowerShell.
Command line arguments rundll32
The next item to look for are the command line arguments that are used for starting rundll32.exe from PowerShell.
If you notice a rundll32.exe process spawned from PowerShell without any DLL supplied as an argument then there is a high chance that something is wrong. By default Cobalt Strike spawns rundll32.exe without arguments but this is also customisable. If you observe multiple rundll32.exe processes (or for example svchost.exe) spawned with the same command line parameters or all without command line arguments then there’s a high chance it’s Cobalt Strike.
32-bit version PowerShell
Review if a 32-bit version of PowerShell was used to launch the subprocesses. You can detect this by looking at the startup path (via cmdline) of the command. If it includes syswow64 (take note of different forms of capitalisation) then it’s using the 32-bit version.
Execute and Write memory pages
You can find injected code with malfind which allows you to find ‘hidden’ or injected DLLs in user memory. Pay special attention to the memory pages that are marked both as executable and writable. Don’t be confused if the first page only contains zeros. Cobalt Strike can will not inject its code in the first page but you’ll be able to find the injected code after the set of zeros.
The default configuration of Cobalt Strike uses named pipes for the communication between different processes:
- \\.\pipe\MSSE-###-server (default Artifact Kit binaries)
- \\.\pipe\status_## (payload staging for lateral movement)
- \\[target]\pipe\msagent_## (SMB beacon)
- \\.\pipe\postex_ssh_#### (SSH sessions)
- \\.\pipe\postex_#### (post exploitation)
- \\.\pipe\####### (7-10 #s) (post exploitation before version 4.2)
The configuration of these named pipes can be changed, but in a lot of cases attackers will just stick to the default settings.
In Volatility you can list the named pipes via the handles command and specify the process PID. Note that the list of named pipes can provide you hints of other machines most likely also affected by the intrusion (for example if the named pipe has references to machine names/IPs for post exploitation or SMB beacons).
For an initial triage and to reduce the volume of results it makes sense to look only
- at processes which normally would not use named pipes;
- for 32-bit processes (syswow32) using named pipes;
- or for named pipes with a seaming-less random name.
As an additional note, the number of characters of the name of the named pipe is a giveaway for what command is being issued. For example mimikatz (8 chars) will have a named pipe name of 8 characters long. Also see the blog of F-Secure Detecting Cobalt Strike Default Modules via Named Pipe Analysis and a list of malleable profiles (including the different pipe names) from Michael Haag.
A last remark on named pipes. Because Cobalt Strike uses named pipes to deliver shellcode you should make sure your sandbox emulates named pipes as otherwise Cobalt Strike might not find its shellcode. See also the blog from NVISO Anatomy of Cobalt Strike’s DLL Stager.
PowerShell logging is essential to further identify Cobalt Strike activity. Because so many attacks make use of PowerShell in one way or another (not only for Cobalt Strike), it is a treasure cave for doing incident response. Unfortunately PowerShell auditing (logging) is not always enabled in corporate environments (as was the case for the IR for which I did this post).
If you do have PowerShell logging enabled, then look for
- DownloadString / IEX downloads for localhost (http://127.0.0.1);
- Large blocks of encoded PowerShell (FromBase64String, EncodedCommand);
- System.Reflection or ReflectedDelegate.
Crowdstrike has an overview of PowerShell log observations of Cobalt Strike commands: Getting the Bacon from the Beacon.
Extract Cobalt Strike configuration
If you have a memory dump of a system running Cobalt Strike then you can use the 1768.py tool from Didier Stevens to decrypt and dump the configuration file of the Cobalt Strike beacon. The tool will scan the memory dump, look for the XOR key and then decrypt and dump the configuration of the beacon. You can dump the configuration in tabular output, CSV or JSON.
Image from https://blog.didierstevens.com/2020/11/07/1768-k/
The dump of the configuration file gives you immediate access to the named pipes, the C2 server (name, port and URI) and the HTTP user agent used for beaconing. All this information is something you can use to further identify compromised devices.
There are a couple of Sigma rules that you can use to detect Cobalt Strike :
- Suspicious Rundll32 Without Any CommandLine Params : Detects suspicious start of rundll32.exe without any parameters as found in CobaltStrike beacon activity
- CobaltStrike Named Pipe : Detects the creation of a named pipe as used by CobaltStrike
- CobaltStrike Service Installations : Detects known malicious service installs that appear in cases in which a Cobalt Strike beacon elevates privileges or lateral movement
PowerShell spawning itself PowerShell spawning (multiple) rundll32.exe processes Multiple rundll32.exe with no command line arguments or all with the same command line arguments 32-bit version of PowerShell started (via syswow64) Memory set as EXECUTE and WRITE List of default Cobalt Strike named pipes PowerShell downloads for http:/127.0.0.1 Encoded PowerShell
These resources provide additional useful information for detecting and fighting Cobalt Strike:
- Cobalt Strike, a Defender’s Guide: A very extensive guide with information on which Event IDs to look for during the different stages, such as Execution, Defense Evasion, Discovery, Privilege Escalation, …
- Awesome-CobaltStrike-Defence: A Github repository with pointers to hunting and detection tools, Yara and Sigma rules and indicators of compromise.