Within the TrickBot framework, there has historically been a loader component. This loader has had continued development over the years since TrickBot’s first release where the ECS key and bot binary were stored in the resource section of the loader . However, the function obfuscation has received relatively little treatment until now.
- TrickBot developers have continued to be active over the years.
- Loader used by TrickBot has had continued development related to obfuscation for anti-analysis.
- The TrickLoader leverages ‘minilzo’ compression, which comes from the LZO library and its usage by these developers dates back to Dyre/Upatre timeframe.
- The goal is to detail the loader and aid additional automation efforts to process the TrickLoader.
TrickLoader obfuscation development timeline:
2017 – Started obfuscating the resource section name
2017 – Custom base64 of strings
2018 – Adds user account control (UAC) bypass , Heaven’s Gate , function obfuscation and further hiding the configuration
Most of these have been reported on in detail with the exception of the function obfuscation, which has been mentioned but not really detailed. Researchers who write scripts for config retrieval have stopped putting them out as frequently as in the past, possibly due to the increased focus by TrickBot to obfuscate and hide the data.
Let’s dive into the obfuscation. The function offsets are stored in a table. The first thing the loader does is execute a call over that table that will push the address of the table onto the stack for the next block of code to use.
The next section will then process the word values from the table in sequence by adding them to a value which is initially the start address of the table and then being pushed onto the stack.
Reconstructing this process into Python code allows us to create the same table as long as we can recover certain values from the binary.
After the function table is rebuilt, a call is made to one of the functions that is responsible for decoding out the other functions and data blobs.
The function decodes the next function. The key is the last value in the rebuilt table address with 0x18 added to it, and the length of the key is 0x327 bytes. Using this we should be able to decode out all the addresses in the rebuilt table.
After decoding all the objects, we can check the sizes of each by printing out the size of every element of the decoded_data list.
Most of them look normal; however, there are a few that seem larger than what you would normally observe in the size of a single function.
These larger decoded objects are actually compressed data. It turns out there are at least 3 compressed objects: a 32 bit TrickBot binary, a large blob of 64-bit bytecode which is the 64 bit TrickBot binary, and a smaller 64-bit EXE file which is a loader for the 64-bit bytecode blob.
The compression is ‘minilzo’, which comes from the LZO library, and its usage by these developers dates back to Dyre/Upatre timeframe. After decompressing the 32-bit binary and fixing the missing ‘MZ’, we have the 32-bit TrickBot binary.
Now that we have the normal TrickBot binary, we can decode out the onboard configuration data which is hidden and XOR encoded inside the bot now. Taking an existing decoder from CAPE  and adjusting it a bit while adding in our deobfuscation works well!
Indicators of Compromise (IOCs)
<mcconf> <ver>1000480</ver> <gtag>tot598</gtag> <servs> <srv>126.96.36.199:443</srv> <srv>188.8.131.52:443</srv> <srv>184.108.40.206:443</srv> <srv>220.127.116.11:443</srv> <srv>18.104.22.168:443</srv> <srv>22.214.171.124:443</srv> <srv>126.96.36.199:443</srv> <srv>188.8.131.52:443</srv> <srv>184.108.40.206:443</srv> <srv>220.127.116.11:443</srv> <srv>18.104.22.168:443</srv> <srv>22.214.171.124:443</srv> <srv>126.96.36.199:443</srv> <srv>188.8.131.52:449</srv> <srv>184.108.40.206:449</srv> <srv>220.127.116.11:449</srv> <srv>18.104.22.168:449</srv> <srv>22.214.171.124:449</srv> <srv>126.96.36.199:449</srv> <srv>188.8.131.52:449</srv> <srv>184.108.40.206:449</srv> <srv>220.127.116.11:449</srv> <srv>18.104.22.168:449</srv> <srv>22.214.171.124:449</srv> <srv>126.96.36.199:449</srv> <srv>188.8.131.52:449</srv> <srv>184.108.40.206:449</srv> <srv>220.127.116.11:449</srv> <srv>18.104.22.168:449</srv> <srv>22.214.171.124:449</srv> <srv>126.96.36.199:449</srv> <srv>188.8.131.52:449</srv> <srv>184.108.40.206:449</srv> <srv>220.127.116.11:449</srv> <srv>18.104.22.168:449</srv> <srv>22.214.171.124:449</srv> <srv>126.96.36.199:449</srv> <srv>188.8.131.52:449</srv> <srv>184.108.40.206:449</srv> <srv>220.127.116.11:449</srv> <srv>18.104.22.168:449</srv> <srv>22.214.171.124:449</srv> <srv>126.96.36.199:449</srv> <srv>188.8.131.52:449</srv> <srv>184.108.40.206:449</srv> </servs> <autorun> <module name="systeminfo" ctl="GetSystemInfo"/> <module name="pwgrab"/> </autorun> </mcconf>