Analysis of an Encoded Cobalt Strike Beacon

I also created a video for this diary entry.

Someone reached out to me for the analysis of a Cobalt Strike beacon. This is the sample.

My tool 1768.py (a tool to analyse Cobalt Strike beacons) isn't able to find the configuration:

When something like this happens, I always try option -r. Option -r is raw mode: by default, 1768.py analyses the relevant sections of a PE file, but in raw mode, it takes a look at the complete file.

But this too doesn't work.

What you can do in a case like this, is execute the sample inside a sandbox, make a process memory dump of it, and then have 1768.py analyse the process memory dump. This often works for obfuscated/packed samples.

But first, I took a look at the PE file with my tool pecheck.py, to see if I could recognize anything that my tool didn't catch.

And there is an overlay (data appended to the end of the PE file). This overlay has a high entropy, and it's 256 KB and represents more than 90% of the total size of the PE file.

Let's take a look at the sections of the PE file to confirm this:

These are indeed all small sections, the largest is 10 KB. So that's too small to contain a stageless Cobalt Strike beacon, but the overlay is large enough.

Let's take a look at the overlay:

That doesn't ring a bell to me. But it seems that there is a repeating byte sequence at the end. Let's take a closer look:

Indeed: there is a repeating sequence of 18 bytes here (I highlighted 2 of them in red and green). This often happens when a PE file is XORed with a key: the end of a PE file is often a series of NUL bytes (0x00), and thus it reveals the XOR key.

I did recover the XOR key with trial-and-error, but I'm not going to explain this here (I do explain it in the video, about halfway). What I did do, is update my tool xor-kpa.py that I use to perform XOR known plaintext attacks. I added a definition for the encoded public key header found in a Cobalt Strike configuration: cs-key-dot.

Let's try that:

And indeed, a key was found, and it's very likely a good key, because 15 extra bytes where found. This means that a repetition of the key found 15 extra bytes matching the cs-key-dot signature.

I now use option -d to let xor-kpa.py decode the payload with the XOR key that was listed as last (thus with the highest probability of being correct), and I feed this output into 1768.py:

And now we have recovered the configuration.

 

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com

DidierStevens

676 Posts
ISC Handler
Sep 6th 2022

Sign Up for Free or Log In to start participating in the conversation!