Introduction to SharpC2

SharpC2 is an open-source (.NET based) command-and-control framework developed by RastaMouse. The main component of SharpC2 is the TeamServer (and related .NET rich client) which is responsible for both generating implants (called drones in SharpC2 parlance) as well as communicating with said implants when they are deployed to the target. SharpC2 contains a rich feature set including SOCKS and reverse port-forwarding making it very useful for red-team engagements.

We won’t go into the details of building and configuring SharpC2 as the docs are excellent. Using SharpC2 is quite simple with the rich client. 

To generate an implant, one first needs to create a handler which defines the protocol (such as HTTP, SMB or TCP) and connection parameters that the implant will use to connect to the TeamServer.

Once you have created a handler you can then create a payload which will communicate using that handler. You can create a number of different payload types including (.NET) .exe and .dll files, powershell script or just raw shellcode (the shellcode is generated via Donut).

Once you’ve generated your payload (e.g. a .exe file) and deployed it to the target you’re all ready to start running remote commands. However on a machine with Windows Defender running, you’ll find that this is what will happen:

In the image above this SharpC2 payload is getting detected by Microsoft Defender as BluntC2… close, but no cigar!  

Blunt or not, our goal is to get past that pesky endpoint security and so the remainder of this article will detail some strategies to bypass these detections against Defender for the .exe type payload. While .exe payloads themselves would rarely be used for initial access (as it is usually too difficult to get past the inherent defences of Mark-of-the-Web and Smartscreen using such a file format), they could well be used for persistence and it therefore instructive to see how to perform a bypass.

And in case you’re wondering why we do all this, it’s simple:  We need to understand how attackers work, and we need to give clients realistic information (not gee-whiz marketing hype) on the effectiveness of endpoint detection, either as part of a red-teaming exercise or as part of a general advisory service.  Knowledge is key!

First attempt!

Let’s see if we can use .NET obfuscation to try to bypass this static detection. The tool we will use is ConfuserEx2. To begin we note that SharpC2 uses .NET Reflection in a number of different places when constructing and executing payloads, so naively applying obfuscation to the executable produced above will result in a non-functional drone. We therefore need to identify the classes where reflection is used and exclude them from obfuscation.

The SharpC2 Visual Studio solution consists of a number of projects, but the main ones we need to concern ourselves with are:

  • Drone: This contains the main functionality of the implant (receiving commands from the TeamServer, executing those commands and then sending the output back to the TeamServer so it can be displayed to the user via the rich client).

  • ExeStager: This is a simple wrapper designed to call the Drone dll reflectively.

  • TeamServer: This is the server-side C2 component which handles communications with the drone. It also receives commands sent by the operator using the rich client and performs the appropriate functionality (e.g. creating listeners, generating payloads or sending commands to implants).

To understand what parts we need to exclude from obfuscation we need to understand how payloads are generated and executed. After the Drone project is built the output drone.dll is copied to the SharpC2\TeamServer\Resourcesfolder. Upon compiling the TeamServer project, drone.dll gets embedded into a TeamServer dll. When the time comes to create a payload the TeamServer\Services\PayloadService.cs class performs the following operations:

  • Obtains the connection information from the chosen Handler (e.g. for a HTTP handler this will be the port and hostname/IP address the drone will need to connect to when checking in).

  • Obtains the byte[] representing the embedded drone.dll and performs the following manipulations:

    • Inserts the handler connection details into the Drone.CommModules.HttpCommModule class

    • Generates a random AES key and inserts it into the Drone.Utilities.Crypto class 

    • Reads from a yaml file pre-defined configuration information about Sleep/Jitter and inserts it into the Drone.Config class

    • Adds the changed drone.dll bytes as an embedded resource to the exe_stager.exe assembly and writes the final payload to disk.

The entry point for the drone is the exe_stager.exe payload which basically just gets the embedded drone.dll and reflectively calls the Execute method in its Drone.Program class:

				
					public static async Task Main(string[] args)
    {
        var bytes = await GetEmbeddedResource("drone");
        var asm = Assembly.Load(bytes);
        
        var task = (Task)asm.GetType("Drone.Program")!.GetMethod("Execute")!.Invoke(null, Array.Empty<object>());
        await task;
    }
				
			
Therefore an initial list of classes to try excluding from obfuscation would include the above Drone.Program (from the ExeStager project) along with following classes from the Drone project (the latter three are not strictly needed at this stage but required later):
  • Drone.Program

  • Drone.Config

  • Drone.Crypto 

  • Drone.CommModules.HttpCommModule

Note that we are only interested in HTTP handlers in this post – if you want to do the same for other handler types you’ll need to exclude their associated CommModules from obfuscation. 

To exclude a class from being obfuscated with ConfuserEx2, you can either use settings defined in an xml-based .crproj file, or more simply through declarative code annotations:

				
					namespace Drone.CommModules;

[System.Reflection.ObfuscationAttribute(Exclude = true, ApplyToMembers = true)]
public class HttpCommModule : EgressCommModule
				
			

Once these code attributes have been applied to the classes mentioned above and the relevant projects re-compiled, you can then re-generate your drone .exe payload and apply obfuscation. The easiest way to do this is using the ConfuserEx2 GUI (obtained from the releases page). 

Once you define the base directory and add the drone exe to the list of modules to be obfuscated, you can edit the rule and use the preset ‘Normal’ level of obfuscation

And once you hit ‘Protect’ your obfuscated .NET exe should be saved to disk in the \Confused folder relative to your base directory.

Now that you have your obfuscated executable you need to check to make sure it is still functional and that the protections such as class/method renaming or control flow obfuscation has not broken anything – this can be done on any machine without EDR installed. The exclusions described above are sufficient to ensure that the drone still works after obfuscation, so now we are ready to run it on a machine with Microsoft Defender running:

Ah – the dreaded Wacatac Machine Learning detection !

So what happened?

Based on experience, the Wacatac ML detections usually appear for executables with high entropy. Running SigCheck on this file confirms that finding:

A entropy value of 7.972 is close to the maximum value (8.0) so the Wacatac detection is not surprising. Can we modify the obfuscation process so that we don’t get such a high entropy value for the final executable? 

To understand if that’s possible and what we need to do, let’s have a look at the unobfuscated and obfuscated versions of the drone in dotPeek: The unobfuscated version appears pretty much the same as it is described above:

The executable contains a simple Program entry class which loads the embedded resource drone.dll.

In the obfuscated version however, the embedded drone.dll resource has gone, replaced by a strange class called <Module>. 

Digging into this class shows a large uint array which contains the drone.dll in some obfuscated/compressed form. What is actually happening is the ‘Resources’ protection mechanism in ConfuserEx2 has been applied, and this protection will take embedded resources in the original file and apply compression and then encryption to them. The resulting obfuscated code will definitely have a high entropy value after those processes have been applied. 

So how do we avoid this?

Second attempt

With the default structure of the drone (a simple Assembly.Load and reflective execution of an embedded dll) the existing protections of ConfuserEx2 are not flexible enough: for embedded resources we only have the ability to either apply Resources Protection (which will just compress/encrypt that resource, resulting in Wacatac detection) or not (which will leave an unobfuscated drone.dll, resulting in a BluntC2 detection).

With the (self-imposed) constraint of not wanting to make significant code changes and just relying on obfuscation to bypass Defender we next try the following strategy: we’ll obfuscate the drone.dll first before embedding into the stager.exe wrapper – this will give us additional flexibility on how the embedded drone.dll is obfuscated. So the process of building the executable will be:

  • Build a clean copy of drone.dll.

  • Obfuscate drone.dll with ConfuserEx2 using the ‘normal’ preset protections.

  • Copy the obfuscated drone.dll into the \TeamServer\Resources folder.

  • Recompile TeamServer, run it and generate a new drone.exe.

The reason why we excluded the classes HttpCommModule, Config and Crypto from obfuscation was so that when the new obfuscated drone.dll was used to create a new stager.exe in PayloadService, those bytecode manipulations described above would not break.

Once we’ve confirmed that the resulting drone is still functional, we copy it to a location with Defender enabled and …. success ! Kind of. The drone checks in once, but then a runtime behavioural detection kicks in:

So, what went wrong this time? 

The entropy of the .exe file was 7.1 which was low enough to prevent a static detection. According to the Defender documentation these behavioural detections are named after the associated MITRE ATT&CK Matrix for Enterprise techniques. The techniques for DefenseEvasion cover a lot of the protections performed by ConfuserEx2. 

So to bypass this runtime detection we could potentially remove individual protections one-by-one and see which is the culprit.

Final attempt

As noted by others with regards to defence evasion, sometimes less is more. Rather than trying to identify which specific protection to remove, we start with the opposite approach: start with nothing and add individual protections until nothing triggers. 

We end up identifying the rename protection as being sufficient.

Obfuscating drone.dll with the above settings, recompiling TeamServer and then generating the .exe as described above gave us what we were after:  A shiny new  implant which was able to bypasses Defender.

Conclusions

In this article we attempted to bypass a common EDR (Microsoft Defender) using a .NET-based implant generated by the SharpC2 command and control framework. This was done by using the ConfuserEx2 tool to apply obfuscation techniques. We observed that we needed to be careful when .NET reflection is in use by excluding certain classes from the obfuscation process. And that the structure of the implant (embedded dll resources) will guide how that obfuscation should occur.

While basic Defender may not be the most advanced EDR on the market it’s probably the most widely deployed due to its default inclusion as part of the Windows operating system. Would the above obfuscation techniques be sufficient to get past a more advanced EDR such as SentinelOne or CrowdStrike ? 

Well, that’s a story for another blog post!  Stay tuned!