Research by: Arie Olshtein
Cyber criminals increasingly rely on packers to carry out their malicious activities. The packer, also referred to as “Crypter” and “FUD” on hacking forums, makes it harder for antivirus programs to detect the malicious code. By using a packer, malicious actors can spread their malware more easily with fewer repercussions. One of the main characteristics of a commercial Packer-as-a-Service is that it doesn’t matter what the payload is, which means it can be used to pack many different malicious samples. Another important characteristic of the packer is that it is transformative – the packer’s wrapper is changed on a regular basis which enables it to remain invisible to security products.
TrickGate is a good example of a strong, resilient Packer-as-a-Service, which has managed to stay under the cyber security radar for many years and continually improve itself in different ways. We managed to track TrickGate’s breadcrumb trail despite its propensity for rapidly changing its outer wrapper.
Although a lot of excellent research was conducted on the packer itself, TrickGate is a master of disguises and has been given many names based on its varied attributes. Its names include “TrickGate”, “Emotet’s packer”, “new loader”, “Loncom”, “NSIS-based crypter” and more. We connect the dots from previous researches and with high confidence point to a single operation that seems to be offered as a service.
We first observed TrickGate at the end of 2016. Back then, it was used to deliver Cerber ransomware. Since that time, we are continually observing TrickGate and found it is used to spread all types of malwares tools, such as ransomware, RATs, info-stealers, bankers, and miners. We noticed that many APT groups and threat actors regularly use TrickGate to wrap their malicious code to prevent detection by security products. TrickGate has been involved in wrapping some of the best-known top-distribution malware families, such as Cerber, Trickbot, Maze, Emotet, REvil, CoinMiner, Cobalt Strike, DarkVNC, BuerLoader, HawkEye, NetWire, AZORult, Formbook, Remcos, Lokibot, AgentTesla, and many more.
Figure 1 – TrickGate over the years.
We monitored between 40 to 650 attacks per week during the last 2 years. According to our telemetry, the threat actors who use TrickGate primarily target the manufacturing sector, but also attack education facilities, healthcare, finance and business enterprises. The attacks are distributed all over the world, with an increased concentration in Taiwan and Turkey. The most popular malware family used in the last 2 months is Formbook with 42% of the total tracked distribution.
Figure 2 – TrickGate statistics during Oct-Nov 2022.
Following is an overview of the attack flow that is commonly found in attacks involving TrickGate.
The initial access made by the packer’s users can vary significantly. We monitor the packed samples spreading mainly via phishing emails with malicious attachments, but also via malicious links.
The first stage mainly comes in the form of an archived executable, but we monitored many file types and delivery permutations that lead to the same shellcode. We observed the following file types at the first stage:
Archive: 7Z * ACE * ARJ * BZ * BZ2 * CAB * GZ * IMG * ISO * IZH * LHA * LZ * LZH * R00 * RAR * TAR * TGZ * UU * UUE * XZ * Z * ZIP * ZIPX * ZST.
Executable: BAT * CMD * COM * EXE * LNK * PIF * SCR.
Document: DOC * DOCX * PDF * XLL * XLS * XLSX * RTF.
The second stage is the shellcode loader which is responsible for decrypting and running the shellcode.
We noticed 3 different types of code language used for the shellcode loader. NSIS script, AutoIT script and C all implement similar functionality.
The shellcode is the core of the packer. It’s responsible for decrypting the payload and stealthily injecting it into a new process.
The payload is the actual malicious code and is responsible for carrying out the intended malicious activity. The payloads differ according to the actor who used the packer.
Figure 3 – Attack flow.
Examples of the different attack flows we observed in the past year:
FEB 24, 2022
Figure 4 – LNK flow
RAR: 3f5758da2f4469810958714faed747b2309142ae
LNK: bba7c7e6b4cb113b8f8652d67ce3592901b18a74
URL: jardinaix[.]fr/w.exe
EXE 63205c7b5c84296478f1ad7d335aa06b8b7da536
Mar 10, 2022
Figure 5 – PDF flow.
PDF: 08a9cf364796b483327fb76335f166fe4bf7c581
XLSX: 36b7140f0b5673d03c059a35c10e96e0ef3d429a
URL: 192.227.196[.]211/t.wirr/XLD.exe
EXE: 386e4686dd27b82e4cabca7a099fef08b000de81
Oct 3, 2022
Figure 6 – SFX flow.
7Z: fac7a9d4c7d74eea7ed87d2ac5fedad08cf1d50a
EXE: 3437ea9b7592a4a05077028d54ef8ad194b45d2f
Nov 15, 2022
Figure 7 – AutoIT flow.
R11: 755ee43ae80421c80abfab5481d44615784e76da
EXE: 666c5b23521c1491adeeee26716a1794b09080ec
The Shellcode loader usually contains a single function which is responsible for decrypting and loading the shellcode into memory. These are the basic steps:
Figure 8 – Shellcode loader – deobfuscated AutoIT version.
Figure 9 – Shellcode loader C version.
In the more recent versions of TrickGate, the shellcode loader abuses the “Callback Functions” mechanism. The loader utilizes many native API calls which take a memory address as an argument of a callback function. Instead of the Callback Function, the loader passes on the address of the newly allocated memory which holds the shellcode. When Windows reaches the point of the registered events, the DriverCallback executes the shellcode. This technique breaks the flow of the behavior we’re monitoring by having Windows OS run the shellcode at an unknown time. In the shellcode loader above, you can see two examples of this in the images “EnumTimeFormatsA” and “EnumSystemCodePagesW”.
Usually, when we find code similarity between unrelated malware families, it is more likely that the actors copied from a mutual resource or shared some pieces of code. For a long time, we noticed a unique injection technique that incorporated the use of direct kernel syscalls, but we didn’t realize the significance, thinking it was probably a fragment of shared code. What caused us to suspect that this unique injection may be controlled solely by one actor is the fact that we saw an occasional “time-off” in operation, and it is very unlikely that several different groups will take a break at exactly the same time. The last break, which was more than 3 months long (from June 13, 2022 to September 26, 2022) was an opportunity for us to verify our suspicion, and dive into the shellcode.
Figure 10 – TrickGate in the last 2 years.
To verify our suspicion, we started to analyze samples across the timeline.
We started our analysis by comparing a fresh sample to an older one. For this test we used
2022-12_Remcos: a1f73365b88872de170e69ed2150c6df7adcdc9c
compared to
2017-10_CoinMiner: 1a455baf4ce680d74af964ea6f5253bbeeacb3de
We know from the behavioral analysis that a similarity exists in the shellcode, so we ran the samples till the point the shellcode is decrypted in memory and then we dumped the shellcode to the disk. Next, we used the Zynamics BinDiff tool (owned by Google) to check similarities in both shellcodes. The results showed a 50% similarity between the tested shellcodes. Fifty percent over a long period of time – more than five years – for quite a large piece of shellcode (~5kb) is unexpected. This automatically raised suspicions that this might be a maintained shellcode, but we needed further evidence in the form of similarity analysis over shorter periods of times to see if it had changed gradually.
Figure 11 – BinDiff result on shellcode extracted 2022-12_Remcos: a1f73365b88872de170e69ed2150c6df7adcdc9c VS 2017-10_CoinMiner: 1a455baf4ce680d74af964ea6f5253bbeeacb3de.
For further analysis, we took random samples from the past 6 years. For each sample, we dumped the shellcode and checked the similarity of the result over time. As you can see in the following graph, the results point to small changes made over time. On the left side we see samples dating from 2016 till 2020 showing about 90% similarity. On the right side, we see a forked version showing a high similarity within itself, but lower similarity with the original version on the left.
Figure 12 – Bindiff result on extracted shellcodes.
We then dived into the gap between the shellcodes to see the impact caused by:
After we cleaned the gap noise, we got the core functionality of the packer. The author constantly maintained the shellcode but used “building blocks” as described in the next section.
Figure 13 – Control flow graph – on the main injection function. Diffing 2016-07_ Cerber: 24aa45280c7821e0c9e404f6ce846f1ce00b9823 VS 2022-12_Remcos: a1f73365b88872de170e69ed2150c6df7adcdc9c
Figure 14 – Diffing kernel direct call of NtWriteVirtualMemory 2022-12_Remcos: a1f73365b88872de170e69ed2150c6df7adcdc9c VS 2016-07_ Cerber: 24aa45280c7821e0c9e404f6ce846f1ce00b9823
As mentioned above, the shellcode has been constantly updated, but the main functionalities exist on all the samples since 2016. An overview of the shellcode’s building-blocks can be described as follows:
API hash resolving.
When we analyzed the TrickGate code, no constant strings can be found. Many times, TrickGate intentionally adds clean code and debug strings to throw off any analysis. To hide the needed strings and its intentions, TrickGate uses a common technique called API hashing, in which all the needed Windows APIs are hidden with a hash number. Until January 2021, TrickGate used to hash the shellcode string with CRC32. In the newer version, TrickGate started using a custom hash function.
The equivalent Python hashing functions used in the last 2 years:
def hash_str_ror1(str): h = 8998 for c in str: h += ord(c) + (((h >> 1) & 0xffffffff) | ((h << 7) & 0xffffffff)) return h & 0xffffffff
def hash_str21(str): h = 8998 for c in str: h = ord(c) + (0x21 * h) return h & 0xffffffff
The following Kernel32 API names have been hashed in TrickGate samples:
API NAME | CRC32 | hash_str_ror1 | hash_str21 |
CloseHandle | 0xB09315F4 | 0x7fe1f1fb | 0xd6eb2188 |
CreateFileW | 0xA1EFE929 | 0x7fe63623 | 0x8a111d91 |
CreateProcessW | 0x5C856C47 | 0x7fe2736c | 0xa2eae210 |
ExitProcess | 0X251097CC | 0x7f91a078 | 0x55e38b1f |
GetCommandLineW | 0xD9B20494 | 0x7fb6c905 | 0x2ffe2c64 |
GetFileSize | 0xA7FB4165 | 0x7fbd727f | 0x170c1ca1 |
GetModuleFileNameW | 0XFC6B42F1 | 0xff7f721a | 0xd1775dc4 |
GetThreadContext | 0x649EB9C1 | 0x7fa1f993 | 0xc414ffe3 |
IsWow64Process | 0x2E50340B | 0xff06dc87 | 0x943cf948 |
ReadFile | 0x95C03D0 | 0x7fe7f840 | 0x433a3842 |
ReadProcessMemory | 0xF7C7AE42 | 0x7fa3ef6e | 0x9f4b589a |
SetThreadContext | 0x5688CBD8 | 0xff31bf16 | 0x5692c66f |
VirtualAlloc | 0x9CE0D4A | 0x7fb47add | 0xa5f15738 |
VirtualFree | 0xCD53F5DD | 0x7f951704 | 0x50a26af |
Figure 15 – API hashing.
Load to memory and decrypt the payload.
TrickGate always changes the way the payload is decrypted, so unpacking solutions that we observe now will not work on the next update. Most of the samples use a custom decryption method but on older samples we also saw known cyphers such as RC4 implementation or the use of Windows APIs for encryption.
Injection using direct kernel calls:
After decrypting the payload, the shellcode then injects it into a newly created process. After the process is created using the create_suspended flag, the injection is done by a set of direct calls to the kernel. For every one of these ntdll API calls:
The following actions are executed:
Figure 16 – Function call graph SYSCALL ID from Manually mapped DLL.
The way TrickGate invokes direct-syscalls is intriguing, as it uses a technique similar to Hell’s Gate. Hell’s Gate is a technique presented publicly in 2020 as a way to dynamically retrieve and execute direct syscall numbers. Here you can find samples dating to 2016 which manage to accomplish the equivalent action to retrieve and execute direct system calls without the need to maintain a System Service Descriptor Table (SSDT).
Figure 17 – SSN dynamically extracted 2016-07_Cerber: 24aa45280c7821e0c9e404f6ce846f1ce00b9823
The injection module has been the most consistent part over the years and has been observed in all TrickGate shellcodes since 2016.
We created strings correlating the most wanted malware in the last 6 years to a single Packer-as-a-Service named TrickGate, whose transformative abilities make it hard to identify and track. Understanding the packer’s building blocks is of crucial importance to detect the threat, as blocking the packer will protect against the threat in an early stage, before the payload starts to run.
Packers often get less attention, as researchers tend to focus their attention on the actual malware, leaving the packer stub untouched. However, the identified packer can now be used as a focal point to detect new or unknown malware.
03d9cbee9522c2c8a267b7e9599a9d245c35c7ac
043ae57e01ebd0a96fa30b92821b712504cfde03
1a455baf4ce680d74af964ea6f5253bbeeacb3de
22f26496f2e8829af9f5cfcd79c47e03fe9a21bb
24aa45280c7821e0c9e404f6ce846f1ce00b9823
30e0181a018fa7dcbd2344dc32adcf77cf840ebe
3437ea9b7592a4a05077028d54ef8ad194b45d2f
3817bad277aa50016e08eed35e92d4a3b5247633
4380044a9517a08514459005836c5f92e4a33871
4f6fa448454b581d6c8e7aa6ed3ef72e66062bf8
666c5b23521c1491adeeee26716a1794b09080ec
75d999d431819311abf8bd048cd084acdcd5f4e1
7f456f8b01fc8866aeed4678a14479b6eaa62fed
975629358bfbba0344ef0dae4d22697ceb2a32b4
977800bd7be3c5c9f2c0dac7f4806e586d8f7b1a
9f20d00b4ec898a33e130720d4d29e94070e1575
a1f73365b88872de170e69ed2150c6df7adcdc9c
a661541c4cbeb1db859f6cec6c53979b5633c75e
afbe838c881e5b223351ff8fa05ddeb3678581ba
b2d58dfee71ce9c509fab1f00ce04c9526c60695
e6dccf4b1fc5ab116b6bc1321346b35dbf42f387
fa5c79321dd4cc2fea795d6ebe2e823abe33ca6f