BitRAT – The Latest in C++ Malware Written by Incompetent Developers

Posted on

To yearn for an HVNC sample that is not ISFB or TinyNuke is a sure sign that you are reverse engineering too much malware.

– Me

 

I was recently made aware of a somewhat new malware being sold under the name “BitRAT” by the seller “UnknownProducts” on HackForums. As far as I know, there has been no public analysis of this malware yet. The seller’s comments indicate inexperience with malware development, as demonstrated by him bragging about using Boost, OpenSSL, and LibCURL in his malware.

The screenshot provided was even more laughable, as we can see the developer used std::thread along with sleep_for. Given the heavy use of such libraries, the malware might as well be in Java. The naming convention is also inconsistent, mixing Hungarian notation (bOpen) with snake_case (m_ssl_stream), with the latter name being copied from an open-source project.

https://i.imgur.com/8tag2tu.png

 

The Tor binary is also dropped to disk, something which no competent malware developer would do. Anyways, enough about the author’s posts, let us move on to analyzing the files at hand. The goal of this analysis is to do the following:

  • Analyze the controller and see how it communicates with the developer’s server.
  • Break the various obfuscation and anti-analysis tricks used by BitRAT.
  • Analyze the behavior and functionality of the RATs and how some features are implemented.
  • Study the relationship between BitRAT and several other malware that it is related to.

The Controller

In this section, I’ll describe BitRAT’s licensing protocol and how the malware controller determines whether the person running it is a paying customer or not. The controller software is developed in .NET and is obfuscated with Eazfuscator. The version I have was compiled on the 17th of August at 11:35:05 UTC.

The licensing protocol starts with the following HTTP request being sent:

GET /lic.php?h=HWID&t=unknown_value&s=unknown_value HTTP/1.1
Host: unknownposdhmyrm.onion
Proxy-Connection: Keep-Alive

The response is the following string, base64 encoded:

unknown_value|NO if not licensed, OK if licensed|0|1.26|1|base64_status_update_message||

If there is no valid license associated with the HWID, the following 2 requests are made to create a purchase order:

GET /step_1.php?hwid=HWID&uniqueid=HWID&product_id=1 HTTP/1.1
Host: unknownposdhmyrm.onion
Proxy-Connection: Keep-Alive

GET /step_2.php?product_id=1&step=2&uniqueid=HWID HTTP/1.1
Host: unknownposdhmyrm.onion
Proxy-Connection: Keep-Alive

If you want to update your HWID, the following request is made

GET /hwid_update.php?hwid_old=[oldhwid]&hwid_new=[newhwid] HTTP/1.1
Host: unknownposdhmyrm.onion
Proxy-Connection: Keep-Alive

The payloads are built on the vendor’s server.

GET /client/clientcreate.php?hwid=hwid_here&type=standard&ip_address=google.com&tcp_main_port=3933&tcp_tor_service_port=0&install_folder=google&install_filename=googleee.exe&pw_hash=535c3fd67cf3e03b01bcc6b7e64e410&tor_prcname=0 Host: unknownposdhmyrm.onion
Proxy-Connection: Keep-Alive

The parameters are as follow:

hwid: self explanatory
type: "standard" or "tor"
ip_address: self explanatory
tcp_main_port: self explanatory, 0 if tor
tcp_tor_service_port: 80 if tor, 0 if standard
install_folder and install_filename: self explanatory
pw_hash: MD5 hash of the selected communication password.
tor_prcname: name of the dropped tor.exe binary. 0 if standard.

The server runs Apache/2.4.29 (Ubuntu) and has a directory called “l” with contents unknown.

The Payload

The main sample that I will discuss is 7faef4d80d1100c3a233548473d4dd7d5bb570dd83e8d6e5faff509d6726baf2. It is written in Visual C++ with libraries including Boost, libCURL among other libraries. It was compiled with Visual Studio 2015 Build 14.0.24215 on the 14th of August at 01:32:11 UTC. The first part of the following section will discuss some of the obfuscation that BitRAT uses, the rest will focus on discussing the behaviors and functionalities as well as how those are implemented.

String Pointers

The file for reasons that are initially unknown stores string pointers into an array instead of using them directly. This is dealt with rather easily using an IDAPython script (attached at the end of the article).

Before (top) and after (bottom) renaming

Dynamic API

Some APIs in the file are loaded dynamically. The code for loading this is quite strange. First, LoadLibraryA is resolved and some DLLs are loaded with it. Then, the author resolved GetProcAddress using GetProcAddress. This highly redundant code is something that no experienced developer would write.

The APIs are then resolved. As we can see from the code the results are strangely not stored at times, for example, in this snippet WSACleanup is never stored anywhere. As was the case before, we dealt with this easily using IDAPython (the name for pmemset shown is automatically generated).

The end of the function is also shrouded in mystery, with the UTF-8 strings for the DLL names being turned into wide-character strings on the heap and then finally returned.

All of these strange quirks didn’t make sense at first, but then it struck me that I’ve seen this done before: this very API loader is a complete paste from TinyNuke. Further examination confirmed this and that some function pointers are not saved due to compiler optimization. Analyzing the code further, one could see that the entire HVNC/Remote Browser portion of BitRAT is a paste of TinyNuke with minimal modification. We’ll go into more details of this in the later section covering the HVNC/Hidden Browser.

String Encryption

Strings are encrypted at compile time using LeFF’s constexpr trick which is copied completely and unmodified. Strangely enough, Flare’s FLOSS tool does not work well on the payload for reasons unknown. As such, other less automated approaches are required for defeating this obfuscation. For this part, I had the help of defcon42 who aided greatly in writing the IDAPython scripts.

First, there are strings that are properly encrypted as LeFF intended.

Second, there are strings that MSVC for reasons unknown (read: being a bad compiler) didn’t perform constexpr evaluation on. For this, we used another script with another pattern.

Third, there are strings for which the decryption function was not inlined (as developers who are well acquainted with MSVC would know, __forceinline is much more like __maybeinlineifyoufeellikeit. Perhaps MS should consider adding the keyword __actuallyinlinewhenforceinlineisused). This is often paired with the second variant of un-obfuscation. For this, we can hook the decryptor function (which are clustered together and easy to find manually) and dump the output and caller address.

There possibly are other patterns that are generated due to compiler optimization that was missed during this process. Since the developer so nicely made use of std::string and std::wstring, I also wrote up a quick hooking library to hook the constructor of std::string and std::wstring and log the string and return address.

With this, we likely have almost all of the strings that are used by BitRAT. There possibly are some strings left over that we didn’t identify, but for the purpose of a preliminary static analysis, this is good enough.

Antidebug

BitRAT uses NtSetInformationThread with ThreadHideFromDebugger for anti-debugging purposes.

Command Dispatcher

The command dispatcher takes the form of a switch-turned-into-jump-table.

The array has 0x88 elements, corresponding to 0x88 unique commands. Initially, I attempted the tedious work of identifying what each of these commands semi-manually, but after working my way through around 30 commands I discovered a function (4D545D) where the list of command strings and their corresponding ID is built. The function takes the form of the following statement being repeated 0x88 times for each command.

Because statically extracting this information would be extremely tedious as the compiler generates code that does not fall neatly into patterns, I dumped the table dynamically through hooking the create_command_entry function. The full table of commands and corresponding ID is listed below:

cli_rc | 00
cli_dc | 01
cli_un | 02
cli_sleep | 03
[...] full list at https://gitlab.com/krabsonsecurity/bitrat/-/blob/master/command_list.txt
hvnc_start_run | 84
hvnc_start_ff | 85
hvnc_start_chrome | 86
hvnc_start_ie | 87

Following this, I’ll be discussing some of the most notable commands and features that the RAT has.

HVNC/Hidden Browser

The HVNC/Hidden Browser feature of this RAT is entirely copypasted from TinyNuke. The following functions from TinyNuke are present in their entirety:

The commands hvnc_start_explorer, hvnc_start_run, hvnc_start_ff, hvnc_start_chrome, hvnc_start_ie are simply copied from TinyNuke with minimal modifications. Below are two side-by-side comparisons of the code to show the level of copy-pasting I’m talking about. The top screenshot is TinyNuke, the bottom is also TinyNuke but inside BitRAT.

TinyNuke (top) and BitRAT (bottom)

TinyNuke (top) and BitRAT (bottom)

One of the most obvious indicators of TinyNuke’s HVNC is the traffic header value “AVE_MARIA” which UnknownProducts did not change.

TinyNuke (top) and BitRAT (bottom)

The HVNC client (located at data\modules\hvnc.exe) is also a complete rip-off of TinyNuke.

BitRAT’s hvnc.exe file

TinyNuke’s HVNC Server project

BitRAT’s “hvnc.exe” file

BitRAT’s “hvnc.exe” file

UAC Bypass

The UAC Bypass uses the fodhelper trick to elevate its privileges. The same code is embedded in multiple functions including the Windows Defender Killer code as well as the persistence code.

Windows Defender Killer

Arguably, this is the most laughable feature of the malware. The first few lines of assembly alone express the sheer absurdity of it.

WinExec? Are we still living in 2006? The function is only around for compatibility with 16-bit Windows!

BitRAT proceeds to run 32 different commands using WinExec to disable Windows Defender. They are as follow.

[esp] 0019F34C "reg add "HKLM\Software\Microsoft\Windows Defender\Features" /v "TamperProtection" /t REG_DWORD /d "0" /f"
[esp] 0019F5F0 "reg delete \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\" /f"
[esp] 0019FD3C "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\" /v \"DisableAntiSpyware\" /t REG_DWORD /d \"1\" /f"
[esp] 0019FBDC "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\" /v \"DisableAntiVirus\" /t REG_DWORD /d \"1\" /f"
[esp] 0019FCC8 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\MpEngine\" /v \"MpEnablePus\" / t REG_DWORD /d \"0\" /f"
[esp] 0019F638 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v \"DisableBehaviorMonitoring\" /t REG_DWORD /d \"1\" /f"
[esp] 0019F6C4 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v \"DisableIOAVProtection\" /t REG_DWORD /d \"1\" /f"
[esp] 0019FE24 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v \"DisableOnAccessProtection\" /t REG_DWORD /d \"1\" /f"
[esp] 0019F3B8 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v \"DisableRealtimeMonitoring\" /t REG_DWORD /d \"1\" /f"
[esp] 0019F2B8 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\Real-Time Protection\" /v \"DisableScanOnRealtimeEnable\" /t REG_DWORD /d \"1\" /f"
[esp] 0019F74C "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\Reporting\" /v \"DisableEnhancedNotifications\" /t REG_DWORD /d \"1\" /f"
[esp] 0019F444 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\SpyNet\" /v \"DisableBlockAtFirstSeen\" /t REG_DWORD /d \"1\" /f"
[esp] 0019F880 "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\SpyNet\" /v \"SpynetReporting\" /t REG_DWORD /d \"0\" /f"
[esp] 0019FA7C "reg add \"HKLM\\Software\\Policies\\Microsoft\\Windows Defender\\SpyNet\" /v \"SubmitSamplesConsent\" /t REG_DWORD /d \"2\" /f"
[esp] 0019FDAC "reg add \"HKLM\\System\\CurrentControlSet\\Control\\WMI\\Autologger\\DefenderApiLogger\" /v \"Start\" /t REG_DWORD /d \"0\" /f"
[esp] 0019FC4C "reg add \"HKLM\\System\\CurrentControlSet\\Control\\WMI\\Autologger\\DefenderAuditLogger\" /v \"Start\" /t REG_DWORD /d \"0\" /f"
[esp] 0019F95C "schtasks /Change /TN \"Microsoft\\Windows\\ExploitGuard\\ExploitGuard MDM policy Refresh\" /Disable"
[esp] 0019F4C4 "schtasks /Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Cache Maintenance\" /Disable"
[esp] 0019FA18 "schtasks /Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Cleanup\" /Disable"
[esp] 0019F8F4 "schtasks /Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Scheduled Scan\" /Disable"
[esp] 0019F52C "schtasks /Change /TN \"Microsoft\\Windows\\Windows Defender\\Windows Defender Verification\" /Disable"
[esp] 0019F808 "reg delete \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run\" /v \"SecurityHealth\" /f"
[esp] 0019F590 "reg delete \"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\" /v \"SecurityHealth\" /f"
[esp] 0019F7CC "reg delete \"HKCR\\*\\shellex\\ContextMenuHandlers\\EPP\" /f"
[esp] 0019FB98 "reg delete \"HKCR\\Directory\\shellex\\ContextMenuHandlers\\EPP\" /f"
[esp] 0019FAF4 "reg delete \"HKCR\\Drive\\shellex\\ContextMenuHandlers\\EPP\" /f"
[esp] 0019F9BC "reg add \"HKLM\\System\\CurrentControlSet\\Services\\WdBoot\" /v \"Start\" /t REG_DWORD /d \"4\" /f"
[esp] 0019F258 "reg add \"HKLM\\System\\CurrentControlSet\\Services\\WdFilter\" /v \"Start\" /t REG_DWORD /d \"4\" /f"
[esp] 0019F198 "reg add \"HKLM\\System\\CurrentControlSet\\Services\\WdNisDrv\" /v \"Start\" /t REG_DWORD /d \"4\" /f"
[esp] 0019F1F8 "reg add \"HKLM\\System\\CurrentControlSet\\Services\\WdNisSvc\" /v \"Start\" /t REG_DWORD /d \"4\" /f"
[esp] 0019FB34 "reg add \"HKLM\\System\\CurrentControlSet\\Services\\WinDefend\" /v \"Start\" /t REG_DWORD /d \"4\" /f"
[esp] 0019F34C "reg add \"HKLM\\Software\\Microsoft\\Windows Defender\\Features\" /v \"TamperProtection\" /t REG_DWORD /d \"0\" /f"

Persistence

BitRAT uses the BreakOnTermination flag through the function RtlSetProcessIsCritical (48563B) to cause a bugcheck on termination of the process. This is done when the command line parameter -prs is present. In addition, it also attempts to elevate privileges using the fodhelper method whenever persistence is activated.

Webcam and Voice Recording

Both of these rely on open source libraries, OpenCV for webcam capture, and A. Riazi’s Voice Recording library with some debugging code removed.

Download and Execute

Usually, I would not discuss such a trivial function, but the malware author managed to write this in a peculiarly terrible way. There are basically two different methods of downloading: the first performs the typical URLDownloadToFile + ShellExecute combo.

The peculiarity lies in the second execution path. Here, the developer opted to use libcurl to download the file to memory and then uses process hollowing/runPE to execute it.

The code is rather clearly copy-pasted, given the use of the default libcurl useragent. In addition, the process hollowing code used was one you would expect to see in 2008 crypters, not 2020 malware.

BSOD Generator

Like the function above, it is also rather trivial and I usually would not bother discussing this. However, even this was completely copy-pasted from StackOverflow.

Configuration

The configuration is edited into the file post-compilation by replacing two strings in the binary. The first string (offset 004C9C68) contains the encrypted configuration information, and the second string (offset 004C9E6C) contains part of what will become the decryption key.

First (004E1694), the encryption key is concatenated with the string “s0lmYr” (we will discuss this further in the next section).

Then (4E16AA), the result is MD5 hashed and truncated down to 16 characters (4E16B8).

Finally (004E16FE), the key is used to decrypt the configuration block. The decryption function uses a class called “Enc”, which is a wrapper around an open source implementation of the Camellia encryption algorithm. Decrypting the configuration of the sample in question (68ac9b8a92005de3a7fe840ad07ec9adf84ed732c4c6a19ee2f205cdbda82b9a4a05ae3d416a39aaf5c598d75bf6c0de00450603400f480879941df9ad9f61f01959d98df31f748e8761d8aa79552c751e208a939d58edf6af7d7215412144355d9dbc1b71567ac895b3fecd3552050b0d1ac6698cf6e43d605f5cabec11853cdd7aa26dfeed45878d12c16eb95cf0805135fb2abab8632e918df7b192946e5d) with the key we generated (ac4016133b9d18e2), we get the final configuration data, which is as follow:

khw3lix3kcivpsmlgglqao2ntut5gmp2ydmvnn5leduil554po5n2wad.onion|0|80|0c9c6aaa257aced0|Xauth|auth.exe|b43e92f859a4b4e81c5c7768339be3e7|Runtime Broker|

We can from this infer that the format is:

hostname|non-tor port|tor port|unknown value|installation folder|installation name|md5 of communication password|tor process name

The unknown value is unique across builds including builds from the same customer. It is possibly used by the malware author to track builds generated by customers but we can’t say much without guessing.

Possible Link to Warzone RAT

Recall the string that was concatenated to generate the key for decrypting the configuration.

As we know, Solmyr is the developer of Warzone, another RAT on HF. The features of the two RATs are somewhat similar, and both are copy-pasted from TinyNuke (Version 1.2 and up of Warzone had the string “AVE_MARIA” from the same stolen code leading incompetent reverse engineers at “threat intelligence” companies [1][2][3][4] to calling it “Ave Maria stealer/RAT” because they couldn’t figure out that this is just TinyNuke’s Hidden VNC).

However, there are a wide variety of differences that indicate that the two are not developed by the same person. First of all, the coding styles of the two are significantly different, Warzone was for the most part lightweight while BitRAT is heavily bloated. The portion of TinyNuke that was copy-pasted is slightly different as well, with BitRAT utilizing the API loading mechanism while Warzone used the regular import table and slightly modified the code as well. Below is the comparison of ConnectServer in the two RATs.

BitRAT

Warzone

Many functionalities are also implemented differently. For example, BitRAT uses SetWindowsHookExW(WH_KEYBOARD_LL) to perform keylogging (004AFD7A), while Warzone uses a Window callback and GetRawInputData to achieve this purpose.

UnknownProducts, the developer of BitRAT, was a customer of Warzone at one point.

It is possible that the developers of the two malware have some form of code-sharing or contractual relationship. However, as there is not much public information available regarding the relationship between the two developers, we could only speculate as to why “s0lmYr” was present as a key in BitRAT.

Final Thoughts and Notes

As is the case with most HF malware, BitRAT is best described as an amalgamation of poorly pasted leaked source code slapped together alongside a fancy C# GUI. It makes heavy uses of libraries such as C++ Standard Library, Boost, OpenCV, and libcurl, as well as code copied directly from leaked malware source code or sites including StackOverflow. The choice of Camellia is somewhat unique, I have not seen this specific algorithm used in malware before.

In marketing the malware, the author makes multiple false claims. He asserted that the malware is “Fully Unicode compatible” while the TinyNuke code used ANSI APIs, he claimed the persistence is “impossible to kill” when in reality BreakOnTermination doesn’t make the process impossible to terminate and can be easily unset the same way it was set. Features such as the Windows Defender killer are terribly done and would catch the eye of anyone monitoring the system, and last but not least, the claim that the developer “[didn’t] copy anything” is most patently untrue, thankfully we “skilled reverse engineers” did in fact “compare signatures and generic behavior”. It is disappointing how easy it is for anyone with minimal programming experience can whip up a quick malware and make a profit harming others.

YARA Rule

rule BitRATStringBased
{
    meta:
        author = "KrabsOnSecurity"
        date = "2020-8-22"
        description = "String-based rule for detecting BitRAT malware payload"
    strings:
        $tinynuke_paste1 = "TaskbarGlomLevel"
        $tinynuke_paste2 = "profiles.ini"
        $tinynuke_paste3 = "RtlCreateUserThread"
        $tinynuke_paste4 = "127.0.0.1"
        $tinynuke_paste5 = "Shell_TrayWnd"
        $tinynuke_paste6 = "cmd.exe /c start "
        $tinynuke_paste7 = "nss3.dll"
        $tinynuke_paste8 = "IsRelative="
        $tinynuke_paste9 = "-no-remote -profile "
        $tinynuke_paste10 = "AVE_MARIA"
        
        $commandline1 = "-prs" wide
        $commandline2 = "-wdkill" wide
        $commandline3 = "-uac" wide
        $commandline4 = "-fwa" wide
    condition:
        (8 of ($tinynuke_paste*)) and (3 of ($commandline*))
}

Hashes

  • 7faef4d80d1100c3a233548473d4dd7d5bb570dd83e8d6e5faff509d6726baf2 (I’ve uploaded this to VirusBay, if you have access to neither VT and VB feel free to message me on Twitter and I’ll share the file.)
  • 278e32f0a92deca14b2a1c2c7984ebf505bbe8337d31440b7f1d239466f4bb74
  • 495bf0fc6abef22302d9ac4c66017fc6c7b767b32746db296ac8d25e77e28906
  • d0abc08b50b1285f484832548dab453203f9b654e2a36c1675d3a9e835419ff4
  • eb82628a61e11bf8a91a687ce55a4615ef3d744635a864aefa7e79c8091ce55c
  • e7860957e268e4cdb8b63a3cf81f450cbfbb31d1cf78e6cc11f6f15cb157b409

Network Indicators

  • TLS certificate with subject matching issuer and CN=BitRAT.
  • Tor traffic.
  • User-agent: “libcurl-agent/1.0” (though this would also be present in some legitimate traffic).

Tools

I’ve published the source code of several scripts and tools I made during the process of reverse engineering. I’ve only published one of the string decryption scripts because the rest are rather unfinished and unreliable. The command hook tool uses the Subhook library. You can view the code on Gitlab.

Comments ( 10 )

  1. NormalUser
    Hi, Great Post What is your suggestion for learning "Reverse Engineering New/obfuscated Malwares"? I've read "Practical Malware Analysis". Do you have any another suggestions? (Book/Tutorial/Video)
    • Mr. Krabs
      I started out a long time ago with Lena's Tuts (not malware specific and somewhat outdated). PMA is good from what I heard, I don't have any book recommendations but the most important part of learning is still learning by doing, keep downloading samples from public sandboxes and write about them.
  2. Guyfawkes
    Looks like BitRAT UI is coded in VB.NET, but not C#. Doesn't seem as if the UI is copy 'n pasted looking at how it uses virtual objects for listview, etc. I mean it seems really unique. Having had a look at most public .NET sources, which RAT is it based on?
    • KrabsOnSecurity
      And the UI happens to be the thing that no one cares about when reverse engineering malware, as it turns out.
  3. Dany
    That thing you mention in the beginning about "m_ssl_stream" is a widely used term based off Boost ASIO documentation and repeatedly used by Microsoft in their examples on github. From the picture over the code showing the usage of "m_ssl_stream" it looks unique and I can't seem to find from where that would have copied. I have to give credits to Unknown for their work even though some snippets may/were copied/pasted here and there. Overall Bitrat seems like a robust RAT and unline other RATS it's very fascinating that not a single complaint has appeared anywhere yet.
  4. Theodore
    Did you offer free malware reverse engineering service on HF with sticky paid thread? Lol. How dare you!
  5. Rennie Allen
    As agent 86 would say: "of course, the old GetProcAddress with GetProcAddress trick".
  6. Sean conner
    lmao https://i.imgur.com/KtifF16.png
  7. Werner Haas
    I am curious about WinExec: couldn't it be that the deprecated function was used on purpose because people tend to forget old stuff? I could imagine such a simple thing being sufficient for flying under the radar of sandboxes from equally incompetent developers.
    • KrabsOnSecurity
      It wouldn't change a thing, WinExec just calls CreateProcessA internally, and for monitoring processes people use kernel callbacks anyways so the API of choice does not matter. https://share.riseup.net/#SX2q4dDzHTQK0-RTsAhpAQ