Researchers have observed TinyLoader, a well-known backdoor, delivering point-of-sale and banking trojan malware over the past few years. Fidelis Cybersecurity Threat Research happened to notice it delivered from a site that hosted a variety of other malware and that was an association we had not seen previously so it prompted us to take a deeper look. This post covers our malware analysis and then describes how our research allows for direct interaction with the command-and-control server.
Tinyloader has a few unique characteristics
- It is, in fact, tiny -- typically weighing in under 5 KB.
- Its command-and-control (C2) server runs on Microsoft Windows, which is fairly uncommon in malware today.
- Finally, it is extremely versatile due to its modular C2 mechanism. This mechanism allows the C2 server to pass custom bytecode directly into the running memory of the bot making it easy to load new malware or augment additional malicious behaviors.
Understanding how TinyLoader functions allows us to mimic bot functionality. Doing this then provides a mechanism to connect directly to malicious C2 servers to monitor commands issued, modules sent, and the running processes targeted by the operators.
In this post, we will discuss TinyLoader delivery vectors, various characteristics of its C2, and finally, how to create a script to mimic the bot functions to monitor C2 activity. We have made our script available on Fidelis Cybersecurity Github(.
The TinyLoader sample we observed was as a secondary infection. A machine that had been infected with Nymaim was later found to be downloading “184.108.40.206/pos.exe”. What’s also interesting about that IP is that the Nymaim malware was downloaded by a BetaBot sample that also came from the IP “220.127.116.11/bbcrypt.exe”. The BetaBot was found downloading Nymaim from “updateservers312.com/jaff/515_new_c.exe”.
For this research, we analyzed this TinyLoader sample: 037c675489bb0faeab114bbd6cf3067a -- though any of the TinyLoader samples mentioned in the IOC section below will provide a similar outcome.
The main part of the bot is XOR encoded and wrapped in a similar fashion. The bot brute forces the XOR key out by comparing it against a known value. The brute force loop provides a custom sleep routine that was likely put in place to cause sandboxes to time out.
The first C2 transmission is a sort of checkin:
The data is in the form of:
The ‘req_num’ or request number is initially set to all NULLs but is then changed by the subsequent downloaded bytecode. This lets the C2 fully control the bot as it sets up what the bot will request next. The ‘campaign_id’ is hardcoded in the bot which is why it is labeled as such. The 0x0c in the picture is the length of the data, while the ‘bit_version’ is set by the bot after a call to IsWow64Process.
The C2 responses are sets of code sequences containing bytecode blobs that will be executed. This is an interesting way to control infections. We broke out the responses into a list (below). Due to the nature of this capability, the bot could essentially download any code to run on the infected machines.
C2 traffic responses(commands):
- Gather process list
- Update Binary
- Install Code Module
Gather process list command
The initial blob of bytecode returned is used to copy a second handler bytecode blob into the memory address provided. The bot then sets req_num to 00 01 00 00. Each new request returns the same sort of setup where bytecode is sequentially written and then a handler is eventually added to initialize the code.
This sequence continues in a list of request numbers.
è 00 03 00 00
è 00 04 00 00
è 00 05 00 00
è 00 06 00 00
è 00 07 00 00 -> this sections detonates the blob of data that’s been copied
When detonated, this copied over bytecode has a whitelist of process names and a mutex. It will attempt to create the mutex and then enumerate all running processes while building a list of those not in the whitelist.
Below we can see a snippet of the code to check each process name to see if it contains a 4 byte string.
We can get a list of all the strings the bot uses to check if it’s not interested in that process.
Data is sent back to C2 with a req_num of 00 0a 00 00 along with the process list appended to the previously mentioned structure. There is also a hardcoded string that is prepended to the data which can be seen in both the bytecode and the traffic.
If the bot sends an Update Binary command, the return is bytecode to copy over a PE updated TinyLoader binary. It then sets the req num to 00 00 c1 00 to begin the next sequence.
The bot then downloads chunks through the following sequence.
- 00 00 c2 00
- 00 00 c3 00
- 00 00 c4 00
- 00 00 c5 00
- 00 00 c6 00
- 00 00 c7 00
- 00 00 c8 00
- 00 00 01 72
- 00 00 02 72
- 00 00 55 01 - restarts after file is written and persistence setup
Next, the newly downloaded bot begins running through the same C2 process again before continuing with the following sequence.
Install Code Module
The bot then goes through the following sequence to download code. The code chunks are saved for later. The code module normally downloaded contains the bytecode that can be used to download other files.
- 01 6e 77 d4
- 02 6e 77 d4
- 03 6e 77 d4
- 04 6e 77 d4
The next step begins a recurring sequence between the bot and the C2 that will do one of two things: It will cause the bot to sleep before its next checkin, or the C2 will issue a command for the bot to do something. In this manner, the C2 can use this as both a way to tell if a bot is 'alive' and either tell the bot to checkin again later or issue some other command.[JR1]
- 00 00 00 54
Understanding the C2 structure can help automate the monitoring of these C2s as we find them and allow us to test certain elements of the traffic. For this part, we’ll be primarily focusing on the ‘\x00\x00\x00\x54’ request because that appears to be the generic checkin request as the bot waits for additional commands. It’s also the request that other PCAPs from previous reports show as returning commands to download additional files.
Let’s start with some basic Python code for sending and receiving data.
Now we just need to account for receiving the data. Since the data length from the C2 is sent within the first 12 bytes we can just receive the first 12 bytes, pull out the length and then account for how much more we should read. Adding the below to our send_msg function.
For the C2 message itself we can just use a Python class to mimic the structure we have previously laid out.
This leaves us with decoding the traffic and then pulling out the relevant data we want. The decode function is a simple XOR loop.
Now we just need to see if this data is the same as the sleep routine but for demonstration purposes we’ll just try to find the next command if it exists and log the data received to screen. For all the recent versions of TinyLoader we’ve gone through the received bytecode appears to always have the ‘mov edx, dword [ebp]’ command right before it loads the next C2 command in place, this could be different for other versions and since we are dealing with bytecode being executed dynamically via C2 it could technically be anything.
The full script will be included with this post. For testing we used a currently live C2 of 18.104.22.168 port 40020.
Decoded this gives us the commonly delivered bytecode that will sleep and then perform the same request again, this makes the bot perform this 'checkin' over and over again until it is given another command.
Awesome, so now we can either expand on this by accounting for all possible commands from the C2 or simply use this script as a way to test for payloads on a live C2 without having to actually run the bot the entire time.
TinyLoader continues to be a widespread threat to global organizations. Here we have discussed TinyLoader capabilities, infector vectors, and how to craft a script to monitor ongoing C2 commands.
Fidelis customers are protected from TinyLoader by a variety of mechanisms designed to detect malware throughout the infection chain.
Nymaim from BetaBot:
Cerber from BetaBot:
Pivoting off IP we can find a large number of exe files being downloaded from this IP.
Running through one of the betabots: fc5af82d8370e248c14765070a1ed1fa
Gives us C2 structure for the campaigns.
Also seen later being used by both SmokeLoader and BetaBot
Also some macro document campaigns related to this IP: