Trojan.TrickBot has been present in the threat landscape from quite a while. We wrote about its first version in October 2016. From the beginning, it was a well organized modular malware, written by developers with mature skills. It is often called a banker, however its modular structure allows to freely add new functionalities without modifying the core bot. In fact, the functionality of a banker is represented just by one of many of its modules.
With time, developers extended TrickBot capabilities by implementing new modules - for example, the one for stealing Outlook credentials. But the evolution of the core bot, that was used for the deployment of those modules, was rather slow. The scripts written to decode modules from the first version worked till recent months, showing that the encryption schema used to protect them stayed unchanged.
October 2018 marks end of the second year since TrickBot's appearance. Possibly the authors decided to celebrate the anniversary by a makeover of some significant elements of the core.
This post will be an analysis of the updated obfuscation used by TrickBot's main module.
Behavioral analysisThe latest TrickBot starts its actions from disabling Windows Defender's real-time monitoring. It is done by deploying a PowerShell command:
After that, we can observe behaviors typical for TrickBot.
As before, the main bot deploys multiple instances of svchost, where it injects the modules.
Persistence is achieved by adding a scheduled task:
It installs itself in %APPDATA%, in a folder with a name that depends on the bot's version.
Encrypted modules are stored in the Data folder (old name: Modules), along with their configuration:
As it turns out, recently the encryption of the modules has changed (and we had to update the scripts for decoding).
The new element in the main installation folder is the settings file, that comes under various names, that seems to be randomly chosen from some hardcoded pool. It's most commonly occurring name is settings.ini (hardcoded), but there are other variants such as: profiles.ini, SecurityPreloadState.txt, pkcs11.txt. The format of the file looks new for the TrickBot:
We can see many strings, that at first looks scrambled/encrypted. But as it turns out, they are junk entries that are added for obfuscation. The real configuration is stored in between of them, in a string that looks like base64 encoded. Its meaning will be explained in the further part of this post.
InsideIn order to better understand the changes, we need to take a deep dive in the code. As always, the original sample comes packed - this time there are two layers of protection to be removed before we get the main bot.
The main bot comes with 2 resources: RES and DIAL, that are analogical to the resources used before.
RES - is an encrypted configuration file, in XML format. It is encrypted in the same way as before (using AES, with key derived by hashing rounds), and we can decode it using an old script: trickbot_config_decoder.py. (Mind the fact that the first DWORD in the resource is a size, and not a part of the encrypted data - so, it needs to be removed before using the script).
DIAL - is an elliptic curve public key (ECC curve p-384), that is used to verify the signature of the aforementioned encrypted configuration, after it is decrypted.
In the first edition, TrickBot was not at all obfuscated - we could even find all the strings in clear. During the two years of evolution, it has slowly changed. Several months ago, the authors decided to obfuscate all the strings, using a custom algorithm (based on base64). All the obfuscated strings are aggregated from a single hardcoded list:
When any of them is needed, it is selected by its index and passed to the decoding function:
Example - string fetched by the index 162:
The deobfuscation process, along with the used utility, was described here. Due to the fact that the API of the decoding functions didn't change since then, the same method can be used until today. The list of deobfuscated strings, extracted from the currently analyzed sample can be found here.
Additionally, we can find other, more popular methods of strings obfuscation. For example, some of the strings that are divided into chunks, one DWORD per each:
The same method was used by GandCrab, and can be deobfuscated with the following script.
Similarly, the Unicode strings are divided:
Most of the imports used by TrickBot are loaded dynamically. That makes static analysis more difficult, because we cannot directly see the full picture: the pointers are retrieved just before they are used.
We can solve this problem in various ways, i.e. by adding tags by an automated tracer. Created CSV/tags file for one of the analyzed samples is available here (it can be loaded to the IDA database with the help of IFL plugin).
The picture given below shows the fragment of TrickBot's code after the tags are loaded. As we can see, the addresses of the imported functions are retrieved from the internal structure rather than from the standard Import Table, and then they are called via registers.
Apart from the mentioned obfuscation methods, on the way of its evolution, TrickBot is going in the direction of string randomization. Many strings that were hardcoded in the initial versions are now randomized or generated per victim machine. For example the mutex name:
In the past, modules were encrypted by AES in CBC mode. The key used for encryption was derived by hashing initial bytes of the buffer. Once knowing the algorithm, we could easily decrypt the stored modules along with their configuration.
In the recent update the authors decided to complicate it a bit. Yet they didn't change the main algorithm, but just introduced an additional XOR layer. Before the data is passed to the AES, it is first XORed with a 64 character long, dynamically generated string, that we will refer as the bot key:
The mentioned bot key is generated per victim machine. First, GetAdapterInfo function is used:
The retrieved structure (194 bytes) is hashed by SHA256 and then the hash is converted into string:
The reconstructed algorithm to generate the Bot Key (and the utility to generate the keys) can be found here.
This key is then stored in the dropped settings file.
As mentioned before, new editions of TrickBot drop a new settings file, containing some encoded information. Example of the information that is stored in the settings:
0441772F66559A1C71F4559DC4405438FC9B8383CE1229139257A7FE6D7B8DE9 1085117245 5 6 13The elements:
1. the BotKey (generated per machine)
2. a checksum of a test string: (0-256 bytes encoded with the same charset) - used for the purpose of a charset validation
3. three random numbers
The whole line is base64 encoded using a custom charset, that is generated basing on the hardcoded one: "HJIA/CB+FGKLNOP3RSlUVWXYZfbcdeaghi5kmn0pqrstuvwx89o12467MEDyzQjT".
Yet, even at this point we can see the effort of the authors to avoid using repeatable patterns. The last 8 characters of the charset are swapped randomly. The pseudocode of the generation algorithm:
Randomization of the n characters:
Example of the transformation:
The decoder can be found here: trick_settings_decoder.py
Slowly improving obfuscationThe authors of TrickBot never cared much about obfuscation. With time they slowly started to introduce its elements, but, apart from some twists, it's still nothing really complex. We can rather expect that this trend will not change rapidly, and after updating the scripts for new additions, decoding Trick Bot elements will be as easy for the analysts as it was before.
It seems that the authors believe in a success based on quantity of distribution, rather than on attempts of being stealthy in the system. They also focus on constant adding new modules, to diversify the functionality (i.e. recently, they added a new module for attacking Point-Of-Sale systems).
ScriptsUpdated scripts for decoding TrickBot modules for malware analysts: https://github.com/hasherezade/malware_analysis/tree/master/trickbot
Indicators of compromiseSample hash: