Understanding & Detecting C2 Frameworks — HARS (HTTP/S Asynchronous Reverse Shell)
Introduction
Hello and welcome to the third blog post in this series about understanding and detecting C2 frameworks.
If you haven't checked the previous blogs please do as we’ve already analyzed both “Ares” and “TrevorC2”
Today we’re gonna take a look at framework created by “onSec-fr” called “HTTP/S Asynchronous Reverse Shell” (HARS). Still in a POC phase but interesting nonetheless. Without further ado let’s get started.
HTTP/S Asynchronous Reverse Shell
Before we dive into the code. Let’s take a look at the general flow of how the framework works (Screenshot taken from their GitHub)
The framework follows a typical client/server architecture. Once the client is registered on the server with a “Hello” request. It then starts querying regularly the server to receive commands.
Similarly to “TrevorC2” this frameworks uses HTML web pages to send its commands within the HTML code. But unlike Trevor it doesn’t clone a websites. But instead uses a bunch of predefined templates that mimic Microsoft’s Bing searches (Yes you read it correctly).
Let’s start by analyzing the server side portion of the code and extract some detections.
HARS_Server.py
The server portion of the code is located in “HARS_Server.py”. As always let’s dive straight into main an go from there.
After initializing some variables. The first function we hit in main is the “InitFile”.
InitFile
As stated before, the framework uses fake HTML web pages as templates to deliver its commands. It comes with at 11 HTML templates simulating different bing searches (Below are a couple of examples)
The “InitFile” function will simply pick a random page from the “template” folder and writes it to disk in a file called “search”.
Note this new file will get deleted automatically if the operators of the C2 sends the “exit” command or presses “Ctrl+C”.
After creating the fake web page. Its time to start the WEB server by calling the “start_server” function.
start_server
HARS uses the python Basic HTTP Server as its Web Server. That means by default the “Server” header will leak the version of python / Base class (Example below)
Server: BaseHTTP/0.3 Python/2.7.18
To avoid this the function start by overriding the “server_version” and “sys_version” to “Microsoft-IIS/8.5” and and empty string respectively. This means that every response coming from the C2 server will contain the following server header:
Server: Microsoft-IIS/8.5
After this it’ll simply start the web server on the port defined by the PORT variable which is defined as 443 by default and assign the cert provided by the “CERT_FILE” variable which points to “server.pem”.
The SSL certificate in question (server.pem) is a self signed cert that impersonate “www.bing.com”. If we compare it with bing’s signed certificate we can identify a lot of differences that can help us detect HARS communication. The easiest to spot is the different “Issuer” and “Subject”. (See screenshot below)
The Web Requests sent to the C2 will be handled by the “MyHandler”. And that’s what we’ll analyze next.
MyHandler
The “MyHandler” defines three functions
- _set_headers
- do_GET
- log_message
Let’s first start by looking at the “_set_headers” function.
This function will simply hard code some headers to be sent whenever the C2 sends a response. We can use these headers and their specific order in correlation with the “Server” header and the SSL certs to confirm that the responses are coming from an HARS C2 server. (See example)
Cache-Control: private, max-age=0Content-Type: text/html; charset=utf-8Vary: Accept-EncodingConnection: close
The “log_message” function is simply overriding the base class function of the same name to handle HTTP logs.
The “do_GET” function is where the magic happens. All the commands sent by the server and the responses sent from the client are handled here.
The first interesting thing this function checks whether the path in the URL contains the word “search”. We can use this as an indicator along with the other indicators we’ll see to determine that this is HARS C2 / Agent.
Now if the URI path contains the word “search”. The handler will branch depending on one of three state conditions read from the cookie header. This is the main method that HARS Server receives information from the CLIENT. By reading the value of a Base64 encoded cookie header.
- Initial Connection (HELLO / SEVMTE8=)
If this is the first time the CLIENT is making a connection to the server. The cookie value will contain the word “HELLO” / “SEVMTE8=” encoded in Base64. If this is the case then the server will respond with a Base64 encoded “HELLO” embedded within a randomly selected HTML page from the templates available.
Note that the commands / responses sent from the C2 will always be concatenated at the end of the HTML pages. This can be used as an indicator to detect HARS behavior.
- Asking for further instructions (ASK / QVNL)
The second command the CLIENT can send is the “ASK” command. The client/agent will send the value “QVNL” in the cookie header every time asking for instructions. If the server find this value, it’ll simply response with a “200” and display the page that contains the command(s) to be executed.
- Exiting Or Sending results back to the server
If the cookie value doesn’t equal “HELLO” or “ASK”. This could indicates one of two things. The C2 operator sent an “exit” command and the agent responded with an “EXIT OK” / “RVhJVCBPSw==” string to signal the end of communication. This will call the “stop_server” function. Which will terminate the server process.
If the cookie doesn’t contain that string. It assumes that the client is sending results and will simply print them to the screen.
Note that each time the server send a response back to the client it’ll generate a new HTML page randomly selected from the list of templates. So basically every response is different to some extent.
The handler logic can be resumed as follows in this graph.
Now that we understand how the handler work the rest of the main function is simple. After starting the server we enter the main loop waiting for the client to connect back.
Any commands the operators will send are simply concatenated to the end of the HTML page.
HARS.exe
The client side of HARS is written in .NET (C#) and must be compiled if want to use it. We’ll take a look at the executable later but first let’s dive into the code.
As we’ve seen from the server side portion of the code we can deduce that the client can execute the following actions:
- Initiate Connection to the C2
- Ask for instructions from the C2
- Send results back to the C2
- Exit / Terminate communications
With that in mind let’s dive into main and start our analysis.
Main.cs
The first action executed by main is the initialization using the “InitializeComponent” function. We’ll get back to it in a minute first let’s continue with main.
The agent performs some checks and sets some attributes that i’ll highlight below :
- Ensure that only a single process with the same name is running at the same time.
- Hide itself by setting the “Opacity” to 0, setting the window state to “Minimized” and removing itself from the taskbar by setting the “ShowInTaskbar” to “False”.
- Create a PowerShell process that will handle all the commands sent by the C2.
InitializeComponent
If we go back to the “InitializeComponent” function. We can see that the “Load” event is handled by the “Main_Load” function. This means that when we double click the agent, its this function that’ll get executed before anything else. So let us take a look.
Main_Load
This is the function responsible for beaconing back to the C2. It start by displaying a fake optional and configurable error message. The default values of this message are read from the “Config.cs” file. (Below is an example of the displayed error).
ErrorMsgTitle = "This application could not be started.";ErrorMsgDesc = "Unhandled exception has occured in your application. \r\r Object {0} is not valid.";
After displaying the message box, it tries to initialize the connection with the C2 using the “Init” function.
Init
This function will simply send a get request to the configured C2 with a cookie value of “HELLO” encoded in Base64. Bellow is an example request using the default values and a server of 127.0.0.1
GET /search?q=search+something&qs=n&form=QBRE&cvid=QVMNZNTKDGWBPWARAMXZGWFJTQNUJXYK HTTP/1.1Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like GeckoAccept-Encoding: gzip, deflateCookie: SEVMTE8=Host: 127.0.0.1Connection: close
The URI value defaults to :
search?q=search+something&qs=n&form=QBRE&cvid=
Only the value concatenated after “cvid” is randomly generated the rest is read from the config file “Config.cs”. But as we’ve seen from the server side all of this is just gibberish to make it look legitimate.The only required parts are the cookie value and the “search” string in the URI.
Once the connection is made the client then will ask for instructions regularly by sending the “ASK” cookie value using the “FetchCmd” function.
FetchCmd
Similar to the “Init” function the “FetchCmd” will request the C2 with a cookie value of “ASK” encoded in Base64 and using the same headers as before. Below is an example request.
GET /search?q=search+something&qs=n&form=QBRE&cvid=IOSAHOBIBKFHYEQUMTGVKSAWXVLXGXUZ HTTP/1.1Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like GeckoAccept-Encoding: gzip, deflateCookie: QVNLHost: 127.0.0.1Connection: close
The command will simply be read from the response and sent to the PowerShell process. The result of this command will be sent back to the C2 via the “ReplyCmd” function.
ReplyCmd
The results will of the command executed by PowerShell will get saved to the “reply” variable and sent via the cookie header encoded in Base64. Below is an example sending back the results of the “whoami” command :
GET /search?q=search+something&qs=n&form=QBRE&cvid=ZHSKOMDXYBOCTPZPJHTXFUOPJJBAVIAH HTTP/1.1Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like GeckoAccept-Encoding: gzip, deflateCookie: d2luLTYyY3B2NXQ2N2FhNjkxXGMyHost: 127.0.0.1Connection: close
That’s pretty much it for the agent communication.
Now as I’ve mentioned at the start. Since this is written in “.NET” we need to compile and create an executable in order to use it. And we know that attackers are sometimes “lazy” as they often use default configurations. Let’s take a look at some of these default configs.
“Config.cs”, Assembly Information And Strings
The “Config.cs” that we’ve glossed over quickly contain some useful information for detection. We’ve looked at some, like the default error messages and the URI path. But it also contain the default callback interval which in this case is between 2 and 5 seconds. This can be useful to correlate between requests.
The agent also sets some default assembly information. This can be helpful in identifying HARS samples with default configurations. (See image below)
Also using the string command on the executable can reveal most of the function names and configuration such as the IP address. We can use this to write a simple yara rule to detect HARS samples.
Conclusion
That’s it for this blog post. Hopefully it was helpful and you got something out of it. If you want to get another perspective from a defensive point of view read the following blog by Lee Kirkpatrick
Until the next one. If you have any C2 frameworks suggestions or any feedback you can find me on twitter @nas_bench
Indicators
Network Artifacts (Agent Side)
- URI : /search?q=search+something&qs=n&form=QBRE&cvid=[Random_Base64]
- HTTP Header (HELLO) : “Cookie : SEVMTE8=”
- HTTP Header (ASK) : “Cookie : QVNL”
- HTTP Header (EXIT OK) : “Cookie : RVhJVCBPSw==”
The following headers must all be present in the request from the agent
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like GeckoAccept-Encoding: gzip, deflateConnection: close
Network Artifacts (C2 Side)
- Response from C2 contain the following headers
Server: Microsoft-IIS/8.5Cache-Control: private, max-age=0Content-Type: text/html; charset=utf-8Vary: Accept-EncodingConnection: close
- SSL certificate impersonating BING search engine
Assembly Information (HARS Agent)
- Title: $Title
- Description : $Description
- Company : $Company
- Product : $Product
- Copyright : $Copyright
- Assembly Version : 1.0.0.0
- File Version : 1.0.0.0
- GUID : aca853dc-9e74–4175–8170-e85372d5f2a9
Endpoint Telemetry
- Look for “Powershell.exe” as a child process of “HARS.exe”
- Look for processes making HTTP(S) outbound connection
Other
Default error messages when launching the agent
- “This application could not be started.”
- “Unhandled exception has occured in your application. \r\r Object {0} is not valid.”