Decrypting Cobalt Strike Traffic With a "Leaked" Private Key
Last Updated: 2021-10-25 06:52:25 UTC
by Didier Stevens (Version: 1)
Cobalt Strike C2 traffic is encrypted with AES. The AES key is randomly generated by the beacon, and communicated to the team server via RSA encrypted metadata. The beacon contains the public RSA key, and the team server the private RSA key.
To decrypt traffic, you either need the AES key or the private RSA key. Unfortunately, as a blue teamer, you don't have access to these keys.
Luckily for us, there are some rogue Cobalt Strike servers on the Internet that use private RSA keys that have been "leaked". These leaked keys can be found on VirusTotal. The packages with these keys are popular with criminals. For more info, read "Cobalt Strike: Using Known Private Keys To Decrypt Traffic – Part 1".
So now that I have a set of private keys, some of them popular, I wanted to find Cobalt Strike traffic that had been captured in the past, and that I could now decrypt.
Brad Duncan, ISC senior handler, as a large collection of malware samples and corresponding network traffic on his site network-traffic-analysis.net. He often analyses samples that involve Cobalt Strike.
So I set out to search a post with a Cobalt Strike beacon communicating over HTTP traffic, using one of the private keys I found. And I quickly found an example after the second try: "2021-02-02 - QUICK POST: HANCITOR INFECTION WITH FICKER STEALER, COBALT STRIKE, & NETSUPPORT RAT".
I will now explain in this diary entry how to decrypt the Cobalt Strike C2 traffic that Brad captured, but if you prefer, I also explain this analysis in a video. That video shows the complete decrypted traffic; while in this diary entry I just show a couple of examples of decrypted packets.
The Cobalt Strike beacon in Brad's capture file, communicates with a team server at IPv4 address 192[.]254[.]79[.]71 over HTTP.
The beacon is configured with the default profile, hence the encrypted metadata is in the cookie of the GET requests:
This cookie is a BASE64 representation of the binary, encrypted metadata. I released a new tool to decrypt Cobalt Strike metadata: cs-decrypt-metadata.py. It takes one or more cookies as arguments, and by default, tries the "leaked" private keys (found in 1768.json) to decrypt the metadata:
As seen above, my tool was able to decrypt the metadata: you can see the machine name and the user name, for example. My tool managed to do this, because the traffic is encrypted with an RSA key pair of which the private key is known (I found it on VirusTotal).
What is important for us now, to decrypt the full traffic, is the raw key: caeab4f452fe41182d504aa24966fbd0. This is a random 16-byte value generated by the beacon, from which the AES and HMAC key are derived.
I can now use my cs-parse-http-traffic.py tool to decrypt this traffic: I pass the raw key via option -r, and I filter the capture for the C2 traffic (option -Y):
The first packet that matches the filter, 6703, causes an HMAC validation error: that's because it is not C2 communication traffic, but downloading of the full beacon by the stager.
The second packet, 9383, is a sleep command, issued by the operator, with 100ms sleep and 90% jitter -> this is a sleep command to make the beacon interactive.
The third packet, 9707, is an unknown command (53) and it results in a directory listing being send back (packet 9723), hence command 53 must be the ls command.
Other examples are some invalid commands issued by the operator:
And a port scan:
To summarize: because I found some private RSA keys for Cobalt Strike on VirusTotal, we can now decrypt traffic of servers that use these keys. These keys can be found in file 1768.json, a JSON file used by my tool 1768.py to analyze beacons and used by my tool cs-decrypt-metadata.py to decrypt metadata.
If you want to know more about the different components of Cobalt Strike, I can recommend blog post "Defining Cobalt Strike Components So You Can BEA-CONfident in Your Analysis".
And I recorded a video for this analysis (included in my Cobalt Strike playlist):