Badrats C2: Initial Access Payloads

Why Badrats?

Badrats is a stage0 C2, designed for initial access on modern Windows platforms. I became motivated to write my own RAT after getting shut down by Antivirus/EDR software one too many times. The features and design in Badrats reflects this need. Below are the "must have" features:

  • Small and simple rat code to aid in AV evasion
  • Provides multiple attempts for operators to load other C2's
  • Usable for Phishing or other Social Engineering engagements
  • Multiple supported payload types for differing situations

Several weeks later, I can say that Badrats has met all of these goals. Specifically, I've been told Badrats does not get caught by most of the "good" EDR, like Crowdstrike, ESET, and Carbon Black. This makes me very happy.

The Badrats comprehensive guide

After downloading Badrats source code, you want to set up your web listener. You can specify the port you want to want to listen on and whether it is HTTP or HTTPS.

Setting up Badrats to use HTTPS and to listen on port 443
Setting up Badrats to use HTTPS and to listen on port 443

Make sure you replace cert/cert.pem and cert/privkey.pem with your domain's public and private keys before starting the listener. If you don't have a valid TLS certificate, you can always use plain old HTTP instead.

Defaulting to plain HTTP and port 8080
Defaulting to plain old HTTP on port 8080

Next, edit the rat code to point back to your chosen port, and public IP or domain name. Make sure to test your payload before sending it.

Changing where the rat calls home
Changing where the rat calls home

Have your victim execute your rat, either through phishing or some other means. If all goes well, you should receive a checkin notification from your rat. You can run rats at any time to check the status of your rats.

New js rat checkin and viewing current rats
New JScript rat checkin and viewing current rats

To interact with a rat, just copy-paste its implant ID. You can run shell commands normally while interacting with a rat. After a brief delay, the rat should check in, grab the command, and return the results.

Interacting with our new js rat
Interacting and running shell commands with our new JScript rat

Use back to stop interacting with a rat and go back to the main menu.

As the name implies, Badrats is known for its perfect stability and will never crash on you. If you don't want to take the chance that I'm wrong, you can always  spawn a new rat. Spawning will create an identical rat in a separate process.

Spawning a new js rat
Spawning a new jscript rat -- Now we have four!
Two wscript.exe processes for two js rats
Two wscript.exe processes for two JScript rats

If shell commands are not enough, all rats support loading and executing Powershell scripts from the attacker machine. To run Powershell on a rat, the syntax is psh <local_path_to_powershell_script> [optional extra commands and arguments].

Running Invoke-Mimikatz from a js rat
Running Invoke-Mimikatz from a JScript rat
Running Invoke-Seatbelt from an hta rat
Running Invoke-Seatbelt from an HTA rat

A few caveats about the psh function: The implementation of psh is different between the PS1 and JScript/HTA rats. The Powershell rat is able to run Powershell in-line no problem (via Invoke-Expression), but getting Powershell to run inside of JScript proved more difficult. After much trial and error, I decided to use a modified version of nps_payload to execute Powershell inside of msbuild.exe.

For operators, please note that using psh on HTA and JScript rats creates an msbuild.exe process. In addition, I've decided to prepend the AMSI bypass in resources/Disable-Amsi.ps1 before any other Powershell code is loaded for js/hta rats. If you want it turned off, set prepend_amsi_bypass_to_psh = False in badrat_server.py source code.

When you use psh, badrats reads the nps_modified.xml template below and shoves the specified Powershell file into ~~SCRIPT~~ as a long base64 encoded string. The whole thing is base64 encoded again and sent down to the rat.

NPS_Modified.xml template file for execution of Powershell by HTA and JScript rats
NPS_Modified.xml template file for execution of Powershell by HTA and JScript rats

When the rat receives psh command data, it decodes it, writes the modified nps_payload XML file to %TEMP%, runs it with MSBuild.exe, then deletes it. Execution results are then set back to the Badrat server.

I had an epiphany after completing the feature above -- If we're using C Sharp to run Powershell, can't we use C Sharp to run C Sharp? The answer is yes, we can! I spent a few weeks of research and came up with CSharper, a simple .NET loader written in C Sharp.

Using System.Reflection, we can load types and methods of compiled C Sharp assemblies and run them! Assemblies can be base64 encoded and shoved into a very long string inside an MSBuild template in the same way psh is. Assemblies are also encrypted before being written to disk to aid in AV evasion.

The syntax to execute C Sharp on your rat is cs <local_path_to_csharp.exe> [optional arguments]. Examples are provided below.

Running Snaffler on a JScript rat
Running Snaffler on a JScript rat
Running winPEAS on an HTA rat -- Look at that cute pea!
Running winPEAS on an HTA rat -- Look at that cute pea!

When compiling C Sharp code to be ran with cs, make sure the class that contains the Main() method, and the Main() method itself are public.

Use the `public` keyword to ensure the `Main()` function is available to be loaded externally
Use the `public` keyword to ensure the `Main()` function can be loaded externally

Lastly, Badrats supports interacting with all rats at the same time to make post-engagement cleanup easier. Type all instead of a specific implant ID to interact with all rats. Type quit to task the current rat, all rats in this case, to shut down.

Interacting with all rats and tasking them to shut down
Interacting with all rats and tasking them to shut down

C2 Design philosophy 101

When designing both the C2 rat and server, we have the option to add complexity to the server or the rat (or both, I guess). Imagine we want to add a feature to get current IP addresses on the host using the ipconfig command and print them out in a neat table.

Raw ipconfig command
Raw ipconfig command
Parsed interface data
Parsed interface data

There are two options to achieve the above:

  1. Let the rat parse out ipconfig data and send parsed data back to the server.
  2. Send the raw ipconfig data back to the server and let the server parse out the data.

There is no right answer here -- each comes with its own pros and cons. If you go with option 1, your network impact will be smaller, but the associated cost is larger rat code, and vice versa for option 2.

Badrats takes method 2 as far as it can go, resulting in a very small ratcode -- under 200 lines of code for every rat. Compare this with the size of Powershell Empire's agent code, which comes in at around 1200 LoC. We can actually see that Empire does its data parsing inside the agent code.

Parsing ipconfig output in the Empire agent
Parsing ipconfig output in the Empire agent

Generally the longer the ratcode, the more static signatures, and the more opportunities AV has to catch us. Additionally, this ipconfig parsing code exists in the Empire agent whether we decide to use it or not.

Another bonus of features implemented on the C2 server is what I call operator choice. Ideally, an operator should decide what level of OPSEC noise is acceptable for them. Dynamically calling down features only when an operator wants them limits the exposure of our offensive tooling and allows the operator to choose what kind of OPSEC footprint they generate.

But Badrats also comes with the downsides of option 2. Long Powershell scripts executed in Badrats via the psh keyword generate some nasty looking network traffic. I recommend avoiding psh and cs in OPSEC-critical situations entirely.

Nasty looking base64 encoded Powershell in plain text web request
Nasty looking base64 encoded Powershell in plain text web request

For this reason execution should be passed off to a more mature C2 framework as soon as possible.

Bug reports, feature suggestions, and merge requests are welcome. Go forth and 0wn 😎

This article was updated on July 11, 2022