One month ago we captured a Word document infected with malicious VBA code, which was detected as WM/Agent!tr by the Fortinet AntiVirus service. Its file name is InternalFax.doc, and its MD5 is
4F2139E3961202B1DFEAE288AED5CB8F. By our analysis, the Word document was used to download and spread the botnet TrickBot. TrickBot aims at stealing online banking information from browsers when victims are visiting online banks. The targeted banks are from Australia, New Zealand, Germany, United Kingdom, Canada, United States, Israel, and Ireland, to name a few.
How TrickBot is downloaded to the victim’s system
When a victim opens the malicious Word document, Figure 1 shows what the document looks like:
Figure 1. The Word document is opened
As you can see, a warning message is shown in the foreground. However, in the background, its VBA code is downloading the TrickBot sample from hxxp://fax-download.com/lindoc1.exe or hxxp://futuras.com/dodocdoddus.exe. Figure 2, below, shows the downloaded TrickBot sample. Its MD5 is
D58CD6A8D6632EDCB6D9354FB094D395, and can be detected as W32/Generik.LWVNLMZ!tr by Fortinet AntiVirus service.
Figure 2. The downloaded TrickBot sample
TrickBot is installed on victim’s system
The original TrickBot is a program developed with Visual Basic 6.0. To increase the difficulty of debugging and analyzing it, the malware developer used a large number self-defense techniques, including code self-modification, code dynamic-extraction, and code/data encryption, etc. Let’s go ahead and see how it works.
When TrickBot is launched it dynamically extracts code from itself, puts it into a heap space, then calls its entry point. The main purpose is to call the Windows API
CreateProcessW to run as a child process with the creation flag “CREATE_SUSPENDED.” This means that when the new process is created successfully, it’s in suspended status. So the malware could get a chance to modify the child process’ code as expected, then send the child process a signal by calling an API to let it resume and run the modified code. This is usually what the malware does to protect its code. Figure 3 shows the calling of the API CreateProcessW.
Figure 3. Call CreateProcessW with CREATE_SUSPENDED flag
As mentioned above, it’ll call
ZwUnmapViewOfSection, ZwAllocateVirtualMemory, ZwWriteVirtualMemory, ZwGetContextThread, ZwSetContextThread and ZwResumeThread APIs to modify the child process’ code. It then modifies the thread context and finally resumes its execution. After that, the parent process finishes its job and is going to exit soon. From now on, the code in the child process will take over and continue the TrickBot’s job.
Let’s move on and see how the child process works.
Actually, the child process is a loader, which loads a named resource from itself into heap space. Of course, the content of the resource is encrypted, but after decryption it appears as an executable code block. Soon the child process will call the executable’s entry point. The named resource is “IDR_X86BOT” or “IDR_X64BOT.” It depends on whether the victim’s system is 32-bit or 64-bit. In our analysis, according to the system type, the named resource is “IDR_X86BOT”. This also affects what executable files are downloaded from the C&C server later.
The code in heap contains the main job of the child process. At first it creates a named mutex object by calling the function
CreateMutex. This is used to check if another lindoc1.exe is running. If yes, it stops doing other things and exits the process. In this way, it can ensure that only one lindoc1.exe can be run at one time. The following ASM code snippet shows how the named mutex object is created.
Next, TrickBot tries to add itself as a task named “Bot” to the Task Scheduler, so that the TrickBot can be executed in a timely manner. Figure 4 and 5 show the screenshots of TrickBot’s task in Task Scheduler.
Figure 4. New Task “Bot” in Task Scheduler
Figure 5. The action of the TrickBot task
The task named “Bot” is able to start “lindoc1.exe” with “SYSTEM” account permission. As you might notice, the original “lindoc1.exe” has been moved to “C:\Windows\system32\config\systmprofile\AppData\Roaming\lindoc1.exe” because this folder is just like “%AppData%” for local “SYSTEM” account.
TrickBot creates a security identity (SID) to check if the user running this process is “SYSTEM”. If not, then it will soon exit the process. See the following code snippet for detailed info on how it checks the account.
Of course, the current account is owned the user who signed into Windows, and not “SYSTEM.” As you may recall, only when TrickBot is executed by the Task Scheduler, the account is “SYSTEM” (see Figure 4.) So the child process exits itself without doing any further things.
TrickBot is executed by Task Scheduler
When TrickBot is executed by the Task Scheduler with “SYSTEM” account permission, it can pass the SID check. It then tries to get victim’s public IP address by sending following HTTP requests.
The public IP address will be used for communication with C&C server later.
It should be noted that most of the data, meaning files generated by TrickBot, are encrypted. TrickBot continually loads encrypted resource data with the name “CONFIG.” After decryption, it contains some information about TrickBot, including its version, group tag, and the IP addresses of its C&C servers. All this information is used to communicate with its C&C servers. If there is already a “config.conf” file, it reads the file and decrypts it to get the “CONFIG” data instead. The content looks like this:
After the IP addresses of C&C servers are received, TrickBot will connect them. I’m going to take one request as an example to show you what the command looks like:
- “lindoc1” is the group tag.
- “AAA-PC_W617600.CA836C89ADF141D19A16BFA7397AD021” is the client id that is generated by current user name, Windows version and 32 random hexadecimals.
- “5” is the command id. According to my analysis, command 5 is used to request downloading something from the C&C server, so the server will reply with data to this command.
- “spk” is an additional information for command 5.
Next, I’m going to show the requests and responses of some main commands in chronological order. In the requests I use “Client_ID” to replace the real long client id in order to reduce the request length. Note that the response data are all encrypted, so I decrypted them here for readability.
[Command 0 request]:
This provides the C&C server with the Windows version, and the public IP address of the victim’s machine. The server then replies with an expiration time and new IP address, which are used to download DLLs later.
“1480550400” is a date/time value. After conversion it’s “16:00 11/30 2016.” It tells us the C&C server’s expiration date and time. The IP address and port “220.127.116.11:447” points to a specific C&C server that holds the DLL files.
[Command 23 request]:
GET /lindoc1/Client_ID /23/1000004/
This sends the TrickBot version to the C&C server to fetch the latest “CONFIG” of the C&C server. When TrickBot runs into any errors in connecting to the C&C server, it’ll send such request. As you can see, the latest version for now is 1000008. It’s going to replace the previous “CONFIG” data as well. Also, the original response data is saved in (or replaced, if it existed) “config.conf,” which is checked first when it’s executed next time.
When the victim’s system type is 32 bit, it sends command 5 to download “systeminfo32,” a 32-bit DLL that is used to steal the victim’s system information. “systeminfo64” is for 64-bit systems. The request is sent to a C&C server, whose IP address and port are obtained from Command 0’s response. In my analysis, it is “18.104.22.168:447.” The encrypted systeminfo32 is saved as “.\Modules\systeminfo32.”
Later, it is executed in a newly-created process, “svchost.exe,” which focuses on collecting the victim’s system information, including its Windows version, CPU type, RAM capacity, user accounts, installed software, and services. Here is the system information collected from my testing system.
Later, the data is sent to a C&C server as body part of command 63 POST request, like this:
This is a command 5 “Get” request to download injectDll32 file from the C&C server whose IP address comes from Command 0’s response i.e. “22.214.171.124:447.” The encrypted injectDll32 is saved as “.\Modules\injectDll32.” In my analysis, this is a very important DLL, which finally is able to inject malicious code into web browsers (IE, Chrome and Firefox) or to monitor the victim’s online banking. I will explain how it works in a later section.
This is kind of a configuration file for “injectDll”. It contains many online banks. The encrypted response data is saved in “.\Modules\injectDll32_configs\sinj”.
This command will going to download “dinj” file. It’s another configuration file for “injectDll” that also contains online bank information. It’ll be saved in “.\Modules\injectDll32_configs\dinj.”
Below is an example.
This command downloads a dpost file from C&C server, which contains another IP address and port that will work together with dinj. When the banks in the dinj file are matched, some stolen bank information will be sent to this IP address. It’s also saved as “.\Modules\injectDll32_configs\dpost.” The content of this file looks like this:
Command 25 is used to get a new link to a bin file. The bin file is going to be the new version of TrickBot. Before exiting this child process, the downloaded bin file will replace the old TrickBot and gets executed by calling the CreateProcessW function. In this way it can update itself automatically. During my analysis I could see that the downloaded bin has been changed many times. They include:
How injectDll steals online banking information
TrickBot keeps updating its config files from time to time. In the latest version of sinj and dinj files, it tries to steal online bank information from dozens of banks.
When injectDll32 is executed by svchost.exe, it enumerates all running processes to check if it’s a browser by comparing process names. See the following code snippet for the details.
From the above code, we know it only focuses on “Chrome”, “IE” and “Firefox” browsers. After it picks one process it uses the process ID to make a combination with a constant string as the name of pipe. This named pipe is then used to communicate between svchost.exe and the browser to transfer the content of sinj, dinj and dport. Then injectDll prepares the code that will be injected into browser, and calls
CreateRemoteThread to execute the injected code. This can be seen in the following code snippet.
On the browser side, it creates several thread functions. One is to communicate with injectDll32 by named pipe, and others are to set Hook functions on some HTTP-related API functions and the keyboard.
It also creates the following registry entries, so that IE can be hooked and monitored better:
- HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3\2500 = DWORD:3
- HKCU \Software\Microsoft\Internet Explorer\Main\TabProcGrowth = DWORD:0
- HKCU \Software\Microsoft\Internet Explorer\Main\NoProtectedModeBanner = DWORD:1
In thread function1, it sends commands to the svchost.exe by that named pipe, to transfer bank information (i.e. the content of sinj, dinj and dpost) to browser. Later in thread function2, it is going to set some hooks on WinINet and Nss3 APIs. In this way, the injected code can capture all HTTP requests from the browsers. Then the local hook functions are able to do further filtering on the HTTP requests with the bank information. If the HTTP request matches the listed banks, this HTTP request will be copied and sent to the C&C server. Let’s see what functions are hooked.
Following 2 screenshots show the original entry code and the hooked entry code of HttpSendRequestA.
Figure 6. Original entry code of HttpSendRequestA
Figure 7. Hooked entry code of HttpSendRequestA
It also sets a global keyboard hook so that it can monitor and collect the victim’s keyboard input. In this hook function it checks to see if the keyboard input is from the browser controls. Figure 8 shows how the global keyboard hook is set.
Figure 8. Set global keyboard hook
I’m going to now provide a real example to explain how the online banking login information is stolen, modified, and sent to its C&C server. The example I’ll use is an online bank that is from sinj. As I understand, “sinj” means static injection and “dinj” is dynamic injection.
Here we go. First, we open IE and go to the login page. Enter testing Customer ID “0903670001” and User ID “1234567890,” as shown in Figure 9.
Figure 9. Online bank’s login page
When we click the “Continue” button, it will send such POST request:
The data is captured by local hook function of
HttpSendRequestW and later it is modified as this:
As you may have noticed, the strings in green are modified or newly added. The string in yellow is the data that I entered on the bank’s login page. It will be sent to the C&C server, whose IP address and port are from command 23’s response.
TrickBot flow charts
Here are the flow charts that show how TrickBot is executed on the victim’s machine.
Figure 10. TrickBot is first executed
Figure 11. TrickBot is executed by Task Scheduler
Through this analysis, we know how TrickBot installs itself on victim’s machine, and how it communicates with the C&C server, as well as what and how it steals online banking information from the victim’s browser, and finally how it upgrades itself from time to time.
Fortinet has published an IPS signature, “Trick.Botnet” to detect the communication between TrickBot and its C&C servers.