Master of RATs - How to create your own Tracker
Master of RATsPreface
One day I was skimming through abuse.ch 1. This website collects user submitted malicious or suspicious URLs and I've stumbled through something very interesting. I saw that a user that goes by the twitter handle @Gandylyan1 is uploading huge amounts of daily samples of the same malware variant called Mozi (You can read about it here 12). This botnet is an IoT P2P botnet that seems to spread like crazy. Gandy is uploading samples as I write this article and there are currently 24,709 IPs uploaded to abuse.ch, and it seems gandy is the only one uploading them.
The malware is very interesting and not too complicated too understand. In the basic gist it spreads through IoT devices using known exploits and brute forcing attacks, if it manages to connect to an IoT device it starts an http service on that device and uploads itself to a random port and hosts the sample on the IoT device's IP address. This peer scans and attacks the network and when it takes over another device this newly infected device will receive the mozi sample from the previously infected peer. This is a parasite. I was so excited about this that I've decided to set off with a simple goal in mind – to build a tracker for this botnet.
But alas my Linux knowledge can be summed up with the fact that I know that " ls -la" should print the contents of a directory. But the idea of creating a malware tracking tool was eating me up day and night. After a short search I've stumbled upon the following tool created by Intezer 1. This tool is found here https://github.com/intezer/MoP 18. This tool is a small python project allows a researcher to fake an infected malware client by simulating an OS environment. All the researcher must do is reverse a malware network protocol. No honeypots, no virtual machines no nothing. Sounds easy right? I though the same. I looked up any open source malware tools on GitHub and found Quasar 20, which is an open source RAT which is used by people for malicious purposes. This is a great way to learn about malware, reversing open source malware and just understanding how everything works under the hood on the networking side. Great candidate for our little experiment! And so, I have set of to become the Master of RATs.
Required Knowledge:
Basic knowledge of WiresharkBasic knowledge of programmingIntermediate knowledge in pythonBasic knowledge in C#
Required Tools:
VMWareVisual Studio CommunityPython 3.8Sublime Text Editor 3DnspyDe4dotBrain
Setting up some goals:
Before we even think about using Intezers tool we must reverse Quasar. What are we looking for?
We want to understand how a Quasar client connects to a serverWe want to understand how Quasar constructs the messages it sends to the severWe want to understand if there is an encryption/decryption process for processing messagesWe also want to understand how the server processes client messages (which is possible in this case since we hold the source code for Quasar)Learning to read C#
We load up the downloaded Quasar source into Visual Studio 2019 Community which can be downloaded for free here and we are greeted with this:
We are interested in the client code, how ever just for reference and as we will be dealing with the others – Common contains various utilities and Server contains the code for the Server application. All C# programs start with Program.cs as far as I managed to figure out so we will start there as well, let's open Quasar.Client and find Program.CS.
Ah, I wish this was C . Anyway, we can see a few things of interest in this little statement if we right click QuasarClient and then click on go to implementation we will drop on where most of the juice happens in this code.
We'll start by explaining the QuasarClient class which inherits from the Client Class. The job of this class is to manage all the events that that accrue within the client, It has a special function to handle the registration of the bot ( OnClientState ), it has a function to handle message reading events ( OnClientRead ) and failing events ( OnClientFail ).
The OnClientState function attempts to send an identification packet to the server. To explore how this message is created we can access the constructor of the ClientIdentification Class
Everything here seems rather normal except for these Proto declarations.
Let's get back to the Program. cs code block and look at ConnectClient.Connect
Which leads us back to the base Client class
Alright! Seems like this the answer to our first goal! It seems that to initiate a connection the client first establishes an SSL Stream, and then it looks like some sort of validation is happening using ValidateServerCertificate callback and AuthnticateAsClient. Let's leave these for now as we are just mapping how the code works. Now what happens next? If we access OnClientState through the Client base class that would lead us to the event handler itself, to find the actual function that triggers on this even we must go to QuasarClient.cs and access the reimplementation of the function through there (Gosh I hate OOP). As we saw before, The OnClientState function triggers the client.Send function
I'll be honest I don't know C# but I'm working with my instincts here (much like in assembly haha) and the only thing of value that I see here is ProcessSendBuffers so let's access that and see if it would yield us any results.
Again, using the same strategy as before, lets access SafeSendMessage and see where it takes us.
Alright, now we don't want to access OnClientWrite as I fear it won't take us where we want but instead lets access WriteMessage which is located within the class called PayloadWriter.
Jackpot. This function writes a serialized message(I'll explain what serialization is, don't worry) to the SSL stream! So, let's make a small diagram detailing our findings:
Alright, this is obviously very shallow and incomplete and as we progress with our dynamic analysis, we could expand on this diagram so let's compile this Quasar Project on Release settings in Visual Studio and move this entire thing to a virtual machine and start playing around with it.
Building and analyzing a sample:
After you compile Quasar and moved it to an isolated environment you can start the Quasar.
This screen should pop up, and this is actually very important. This pop message is a builder for the X509 Certificate which is responsible for creating a valid SSL stream between the client and the server. Quasar will generate a X509 cert and bind this cert to all generated clients. You can learn more about SSL here: LINK 1
After you generate a certificate is time to build a sample, after generating the certification click on the Builder and you should be promoted with the builder menu, the most important part is this one:
I have 2 IPs here; one is the loop back address and the other is the local IP of this Virtual Machine. I would suggest binding the client to the IP of the current virtual machine as it would be possible to emulate connections to the server from the current virtual machine and outside through the host (since the host is also a member within the VMWare local network). You can use any port you like but I've used port 27015 because Minecraft. After you built the client you should see it within the current directory of the installed Quasar client. Let's take it out and open it in dnspy which is a .NET decompiler
But we are met with this garbage, but do not worry! We can use de4dot which a .NET binary de-obfuscator so let's run it and we should be met with a clean Quasar client:
So what you can see here, is although our Quasar client is clean the symbols are gone but don't worry as we hold the full source so let's start debugging. we just want to see if our diagram is correct so lets click start and place a break point on the entry point (I highly suggest renaming these functions and class names according to the source but since I've debugged this so many times I already know this know block like my right hand). PLEASE MAKE SURE QUASAR SERVER IS RUNNING. We'll encounter our first problem with in Class0.smethod_3() which is the second Initialization method:
It will not return True , thus causing the client not to execute and exit. but why?! Let's look inside our source code:
This if statement which is marked in red, install and connects our client to the server by returning true after initializing but it seems it will not execute as the current path the client is running from is not equal to the install path. To understand what I mean let's go back to the builder:
So, this code block checks if the client is currently being run from inside Appdata\Romaing (In this specific case) if its not there it would execute the following code:
This code handles two problems, one is that the client has detected that another instance of Quasar is running because it detected the same mutex that was used within the current client and the other is to Install the client into the computer but adding persistence, killing and deleting the current file and process and relaunching it after it has been moved to our designated install folder. You can enter the Install method and read for yourself as the code is very documented. This is very cool cause it gives a researcher a real insight in how malware might be developed. With this knowledge in mind let's do two things:
Update our diagramMove our client to the designated install directory and start it from there
Let's debug our client from our preferred install directory and see what happens, remember to make sure the quasar server is running in addition I would like to fire up Wireshark to monitor the network(This are my own settings, and the IP address and Port will be different on your machine):
I'll restart the client from the Appdata\Roaming directory and jump straight into the Client.Connect function:
This time we hit exactly where we want (Pro tip, you can right click dnspy objects and change their names but hitting Edit method, after hitting enter to confirm the change it would send you inside the edited function – to go back, press backspace).
We have three places that are of value here, first is the RemoteCertificationValidationCallBack which would validate the certification received from the server, the stream reading function and OnClientState function that as stated before should send us to the QuasarClient registration handler.
So socket.Connect function should connect me to the server successfully and initiate the first TCP handshake :
Next I want to examine what happens when we execute line 287.
This is an SSL handshake, but what happened is that the server passed its X509 cert to the client and the client approved this certification, and this is happening inside RemoteCertificationValidationCallBack. Let's examine how it looks inside the source code
As you can see within the # else statement which happens when the binary is compiled with debug mode off, there is a function that checks if the clients and the servers certificate match. But look at what happens within the debug mode, it just returns true and because this happens on the client side… our client emulator can do the same to initiate a valid SSL communication with the server. Let's keep this in mind and continue. What happens next is a bit tricky, in line 290 inside the Client Class, OnClientState would be called but because it is called from the Client class the event registration function would hit and not the event handler function.
We must find the QuasarClient class manually and from there navigate to the OnClientState** function**(I advise the reader to read this a few times and to play around with the source code to fully understand what this means as this is very important to understand how the client behaves, and having the source code is just a privilege to expand on our researching and coding skills). "But Danus! How will we find it in this mess of unnamed functions? " The answer to that is very simple, let us return toClass0 which is Program.cs:
So Gclass27 is QuasarClient , lets rename it so it would be easier to navigate to it, then we'll access this class by double clicking it and try to find OnClientState Manually.
Here it is, let's place a breakpoint on line 79 , and set a breakpoint inside the PayloadWriter WriteBytes function we found earlier which is located inside dnspy under Stream1, method02. On line 79 Class18 is created, and then passed into the send function. Class 18 is called ClientIdentification within the source code of Quasar:
Which is the message constructor and if we continue the execution up until the payload writer, we can see the contents of this message:
And we just intercepted the entire message. Easy. But how ever I do want to note something strange, there are only 14 members inside the ClientIdentification class but here inside the debugged message there are 28?
In addition, in line 38 the message gets copied into a stream and serialized then the length of the message is sent and then the raw bytes returned from the serializer function are sent. What in the hell is a Serializer and why are there 28 items in the message protocol when there should be only 14? Also look at the contents of the message after it gets serialized. First let's debug the program until line 40 and view the contents of the array variable by right click it and then clicking on show memory window.
One can recognize some the message text but there are so many extra bytes here that just don't make sense at all.