Cobalt Strike Remote Threads detection

Olaf Hartong
Nov 29, 2018 · 3 min read

Update Nov 30 2018:> Found a way to change this behavior on Cobalt Strike, added at the bottom

Update Dec 6 2017:> The splunk app is available now here

I was playing around a bit with a cool new C# tool one of my colleagues created, NoPowerShell. This allows an attacker to execute certain PowerShell commands from Cobalt Strike without having to use PowerShell itself.

As a blue teamer I obviously want to be able to detect this, so I set to work in the lab and started looking at where Sysmon could help me out. Since NoPowerShell relies on Process Injection to do its business I started looking at the “Create Remote Thread” events and soon I noticed something interesting.

Every process injected bij Cobalt Strike is injected into a memory address which is starting from the same last 4 bytes on every thread.

Image for post
Image for post
Every injected thread ends with 0B80

I’ve tried this on several hosts and changing the malleable profiles to use different target processes but this behavior seems to be consistent.

Detection

  • EventCode / event_id 8
  • StartAddress / target_process_address ending with 0B80

In this screenshot I’ve been using a PowerShell beacon, this can be anything so using this as an indicator is pointless, as with the target process.

I’ve incorporated it into my ThreatHunting app, which will be released at BlackHat EU next week on Dec 5th. A detection of the event will look like this:

Image for post
Image for post

Drilling deeper into that event will show;

  • a visual representation of the injection,
  • all subprocesses spawned by powershell.exe
  • the originating process, launching the beacon,
  • the beaconing traffic
  • and the remote thread events.
Image for post
Image for post

I’ll keep working on ways for Cobalt Strike to not cause this issue as well as digging deeper into the in-memory part of the processes to be able to detect them.

Evasion

{ stage
transform-x86 { # transform the x86 rDLL stage
prepend "\x90\x90\x90\x90\x90\x90\x90\x90\x90"; # prepend 9 nops} transform-x64 { # transform the x64 rDLL stage prepend "\x90\x90\x90\x90\x90\x90\x90\x90\x90"; # prepend 9 nops}}

Obviously there are variations possible here. The point is you, as a red teamer, want to be invisible. By adding 9 nops only the last character of the Start Address / target_process_address of the injected thread will change. There might be a point by when adding too many null bytes can cause instability.

Image for post
Image for post

Detection strategy

On top of this baseline injection behavior in your environment, this is not that common that you get swamped by data anyway. Create an alert on outlier processes receiving injects by uncommon sources. Also be aware or lateral movement before discounting multiple hosts with similar behavior.

Using a Cobalt Strike Malleable profile will be a global setting so again the Start Address / target_process_address of the injected thread will be identical across all systems targeted by this method.

Thanks to c_apt_ure, Scouby and mika for validating detection in their environment. Thanks to @_vivami for providing a mallable profile to evade this.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store