SmokeLoader's Plugins
Introduction to SmokeLoader's Plugins
SmokeLoader is a well-known malware family that has been around for more than 10 years. Its main purpose is to download and drop other malware families. However, SmokeLoader's operators also sell plugins that add capabilities to the main module. Those plugins allow an affiliate to collect browser data from infected computers, as well as emails, cookies, passwords, and much more.
In this blog post, we'll dissect SmokeLoader's plugins that were received by an infected computer from the botnet "0020". We'll be exploring their inner workings, capabilities, and threat vectors.
SmokeLoader's Plugins analysis
Plugins decryption
SmokeLoader sends encrypted plugins to the boots with a message that starts with a header that specifies the number of plugins, the combined size of them, a plugin marker, and a plugin ID. The header is followed by one or more plugin structures that specify the plugin size, its 15-byte RC4 key, and the encrypted plugin content. This was previously documented by Pim Trouerbach in his talk at BSides Portland 2022. Figure 2 is an overview of what's sent to the bot by the Command and Control (C&C).
Figure 2 - Message header
Decrypting the encrypted content using the 15-byte RC4 key leads to the final content of the plugin.
PE reconstruction
After decryption, we noticed that plugins are in unusual formats. All PE headers have been removed, so we need to reconstruct those in order to statically analyze plugins. As illustrated in Figure 3, the first two double words (dwords) correspond to the offset of the Portable Executable header within the file and the architecture of the plugin, 0 meaning 32 bits, and 1 meaning 64 bits.
It's important to note that the alignment of PE data naturally mirrors that of a genuine Portable Executable following the architectural specifications. Consequently, the header can be manually reconstructed by adding missing fields, resulting in the creation of a fully valid Portable Executable file.
Figure 3 - Plugin header
The resulting payload is packed using UPX, however, attempts to unpack it using the same tool were unsuccessful, likely due to the removal of certain metadata from the original file.
The unpacking procedure can be done by setting a breakpoint at the end of the UPX code, then dumping the in-memory image, and adjusting the Import Address Table using Scylla, and finally modifying the entry point.
Diving into code
We obtained a total of 8 plugins, with 4 being 32-bit and the other 4 being 64-bit. We saw unique functionalities on 5 of the plugins, meaning there were 3 pairs of plugins that had the same functionality, but were implemented in both 32 and 64-bit.
Upon reception of a plugin, the main module creates a new process, maps the plugin in memory, and sets three parameters for its entry point. The first two parameters can be likened to "hinstDLL" and "fdwReason", similar to those used in DllEntryPoint. The first parameter represents the base address of the module, while the second corresponds to DLL_PROCESS_ATTACH. The third parameter, on the other hand, serves as a pointer to a malware-related data structure previously copied into memory by the main module.
The structure can be described as the following:
struct plugin_struct
{
BYTE is_injected;
uint rc4_encrypt_key;
char bot_id[41];
uint main_module_process_id;
char c2s[260];
char user_agent[552];
uint size_plugin;
BYTE encrypted_plugin[size_plugin];
};
We can distinguish two different types of plugins:
- The ones that steal data from disk. They usually run once, then terminate. Their purpose is looking for specific files on disk, and sending those to the C&C server.
- The ones that get injected into processes and steal data directly from memory. Usually the plugin hooks a key Windows API (send, WSASend, …) within process memory, and sends the collected data to the C&C server. We'll call those plugins "Hook plugins".
Process injection
When a Hook plugin starts, one of the first checks that it does is to look for the value of the first byte of the malware structure.
Figure 4 - injection byte check
When the main module runs a plugin, this value is set to 0, so the plugin will run the function fn_map_itself_in_processes().
The injection process is the same for all Hook plugins. The plugin embeds a hardcoded list of processes names that should be injected if they're found running on the infected client.The names of processes are hardcoded within the plugin.
For each process to inject into, the plugin will decrypt the raw bytes that were passed by the main module within the plugin_struct (field encrypted_plugin). Those raw bytes are the plugin's bytes in its original format, without the Portable executable header.
The plugin then creates a section using NtCreateSection, used to map the plugin in the target process. The plugin will then map the section in its own process and in the target process, using NtMapViewOfSection, and will perform all the loading work on the map view that it created in its own process. It'll be mirrored in the target process. Once the plugin has loaded sections and resolved the import address table, it calls LdrProcessRelocationBlock() to apply relocations and finalize the injection in the target process.
Also, the plugin will map a small trampoline (Figure 5) and the plugin_struct (with the is_injected byte set to 1) in the target process using the same procedure. Since the plugin code needs 3 arguments when starting, it'll replace the values in RCX with the base address of the plugin, R8 with a pointer to the plugin_struct, then RAX with the entry point of the plugin.
Finally, the plugin will make a call to the API CreateRemoteThread within the target process, with the start address being the first byte of the trampoline.
Figure 5 - Trampoline
Hooking functions
In order to gather valuable information, plugins use hooking to hijack the control flow that enters specific windows API. This is done by replacing the first few instructions of a function with a trampoline that jumps to the hooking function. That way, valuable information, like arguments that were passed to that function, can be sent to the C&C.
We won't go into much detail about the hooking procedure, since SmokeLoader's plugins implement it in a pretty standard way.
Plugin's SHA256 | Architecture | Description |
---|---|---|
e45f05c69821061719fbbfecd107db82- 00429ea23753d2b6106a9c6f74ffa47f |
32 |
|
36d59dcdf6860e27d7238b583e67560d2ed- 7795c4eaa884c10f0c5d20af53d79 |
64 |
|
47bff089e05cafe0f0384e07ed4ce02ae45740- 3af921054aff837df3bfeb67fc |
32 |
|
ad073b2d3e1702b573263718bcacddbe134bc9c- e8f2cc1e03e14525e588117e7 |
64 | 64 bits version of 431c9e7fbceac89eeac9a61e- b3bca78dd44a1241 |
1bd225e2a4bd9cfdab22811b4d072d99ac33- 25a6c7b7b64182f3ca04ccd43ccb |
32 |
|
fdc148593ee71e5cb47170757fc4c930c1cf- bc7d97722ae503a5cb0632364877 |
64 | 64 bits version of 1107011f4e5dd3b05b8dc29df- 8e089564acfbe3a |
5339bda196e6090309995c18f83fab58d570fa3- 564f5c6afae9b72ca9f66140f |
32 |
|
a0a1d60b2fc491e1c6c2a8e5db1d5566297926e5- f97c06c71a0c10cc364ef083 |
64 | 64 bits version of 4479b9b4edf1d5cb7affc- 0622d91b23e6274030f |
36d5…3d79 is not exactly the 64 bits version of e45f…a47f. The 64 bits version of it might have encountered some problems at execution so they probably removed functionalities.
Summary
In this blog post, we've taken a closer look at SmokeLoader's plugins and explored what they can do. Originally, SmokeLoader was designed to do one thing: download and run other malicious software on infected computers. But now, it has an extra feature where it can download and use these plugins sent from its C&C. These plugins allow attackers to steal important information, either from the computer's memory or from files saved on the computer. The ones we've examined mostly focus on gathering data from web browsers and email. It's essential to keep in mind that there might be more of these plugins available because SmokeLoader operators sell them, and not every botnet may have all of them.