Detecting Cobalt Strike by Fingerprinting Imageload Events

UPDATE — Check out a related detection technique to find execute-assembly activity

While 2020 has been pretty miserable for many people one small silver lining is this year I’ve been fortunate enough to engage in multiple Purple Team exercises affording me the opportunity to observe some pretty interesting TTP courtesy our Adversary Emulation team. Quick background…


Cobalt Strike, for those of you living under a rock, is a commercial penetration testing platform, developed by Raphael Mudge, used by many of today’s elite Red Teams and, unfortunately, nation state and criminal threat actors. For about $3,500 a bad guy gets access to a very advanced post-exploitation tool. Its basically Metasploit except 100x better — hence why you pay for it. A lot of criminals and APT groups have cracked this software and run pirated versions to infiltrate networks. In my opinion, the greatest strength of Cobalt Strike (henceforth CS) is that it gives attackers the ability to perform most of their operations within memory and/or using native Windows API calls. According to MITRE’s ATT&CK matrix, at least 9 APT groups including APT32, FIN6, and the Cobalt Group utilize this tool in their arsenal.

Back to the detection…

Since CS has the ability to perform most of its activity within memory space organizations either need to have an advanced EDR solution that can monitor process memory for malicious activity or detect the beacon executable’s actions on the endpoint. For example, the CS beacon will spawn a sacrificial process then hallow out its memory and inject whatever malicious code it needs to execute into that process. By default, CS will spawn rundll32.exe, however, this is easily customized to be whatever. But, at the end of the day, the beacon has to execute at least 1 time on disk. When it does, it will load specific DLLs and will reach back out to its C2 servers to establish a connection. This behavior is what we want to zero in on.

Loaded DLLS

To figure this out I will take a peek at a subset of the CS beacons launched by our Red Team during the last few operations where 7 different beacons were utilized.

We can see that the beacons all share a similar number of DLLs that are loaded when they are executed (~60). Now we need to figure out which DLLs they have in common.

To do that we’ll filter by file_name (which in this dataset is the name of the DLL loaded into the process).

This provides us with 56 unique DLLs loaded by CS beacons when they are first launched.

We can test that really quick to make sure we’re detecting the beacons we know about.

It works!

Now, these DLLs are loaded by many other processes — so we need to add some additional filters to ensure we’re capturing ONLY processes that have network traffic associated with the process.

When it comes to statistical analysis/detection I’ve found using historical data very helpful in weeding out common processes within one’s environment. If we combine that methodology with the assumption that bad guys want to be stealthy and wont blast 100s of phishing emails with links to download their beacons we can filter processes we’re not interested in by looking for processes that show up rarely within an environment. How do we accomplish this? In Splunk — using distinct count via stats…

The above where filter takes results and looks for processes on 3 or less hosts with the same md5 loading at least 54 of the 56 DLLs we identified previously.

The next step in filtering out noisy non-CS beacon processes is to only worry about processes making network connections. There’s a few ways to accomplish this (via join/subsearches), but, I’m always a fan of optimizing our SPL so I’m going to use tstats and common fields to get all the processes loading these DLLs using the above where filter as well. This is how that looks…

Its a lot of SPL — but basically its taking the individual sourcetypes the EDR log types are split into — combining them into 1 tstats search then ONLY displaying results where a process has loaded at least 54 DLLs, has network traffic (url/tcp) is on 3 or less systems, located within user writable file paths (folders).

This is nice — but, if you’ve been following my other posts you know I’m a fan of enriching data (specifically VirusTotal and other threat intelligence sources). You can find those other stories: Part 1 and Part 2. By enriching data we have additional attributes to filter false positives.

Using the above SPL I’m querying VirusTotal (via v3 API) to lookup information about a hash value. One of the fields our lookup brings back is sig_info which provides information relevant to a binaries digital signature (basically whether it was signed or not and if it was who is the CA). Assuming most attackers are unable to get their beacon’s signed by a reputable CA the above SPL ignores any binary that is signed. This drops the false positive ration down pretty dramatically as almost all Microsoft binaries will be ignored.

We’re left with what we’re after — the same processes we already knew our Red Team was using — Cobalt Strike beacons galore!

Pair this detection with a previous detection (detecting non-browser processes communicating with newly registered domains) and you just made an adversaries job that much harder.

Disclaimer — our Red Team never runs CS using the default configurations — however, the EDR tool deployed throughout our environment is pretty good about picking up generic default CS beacon activity.

Cyber Security enthusiast, detection developer and engineer, researcher, consultant.