Introduction
In this blog, we will continue our work on malware analysis of the Trickbot malware family. In the last post [001], we analyzed a series of droppers, which used different techniques to try to evade AV/EDR and get their payload executed on target. We were unable to retrieve, what I'm assuming is the last stage of the execution chain. So for this post, I found a sample of one of the Trickbot agents.
This analysis will have us look at even more droppers and finally the persistence mechanism launching the agent. We will be looking at more methods and tools for analysis including online sandboxes and then in the end see the value of manual analysis as we are able to uncover a lot more IOCs from our analysis than what we got from our initial triage using an online sandbox.
Sample
Since I was unable to find the file dropped by the series of droppers from the last post, I went searching for something equivalent to what I guessed was the next step. After a bit of looking, I found this file “bookworm_ms.exe” on malware bazaar [002], running it through an online sandbox, it showed that the file did some malicious behaviour and then had what looked like C2 communications. This was sufficient for the analysis I'm going for here, so let's get started with the analysis.
Bookworm_ms.exe: Dropper
Initial triage
For the initial triage let's use an online sandbox, in this case, I choose ANY.RUN [003], and as described earlier, the result looks promising, we have some file creation events, processes spawning other processes and finally some HTTP communication.
So now we have a general idea of what is happening. That is cool, but what if we wanted the same info, but without uploading malware to an online sandbox? A good alternative is prodDOT, using this we get a nice graph of the different actions taken by the malware
Here we see everything as displayed in ANY.RUN. What ANY.RUN does is that it applies some nice rules for detection on top, but nothing that can't be figured out manually. To get a better idea let's step through the execution flow from procDOT:
- Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\__tmp_rar_sfx_access_check_1933125
- Delete file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\__tmp_rar_sfx_access_check_1933125
- Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\MsMpEng.exe
- Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\MpSvc.dll
- Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\readme.txt
- Create process: MsMpEng.exe
- C:\Users\IEUser\AppData\Local\Temp\RarSFX0\MpSvc.dll loads thread 4852
- thread 4852 reads C:\Users\IEUser\AppData\Local\Temp\RarSFX0\readme.txt
- thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\MsMpEng.exe
- thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\MpSvc.dll
- thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\delete.txt
- thread 4852 creates file: C:\ProgramData\Microsoft\RSA\MachineKeys\sgkey.data
- thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\MpSvc
- thread 4852 creates C:\Users\IEUser\AppData\Local\Temp\bpu.ini
- thread 4852 creates C:\Users\IEUser\AppData\Local\Temp\bpu.dll
- thread 4852 creates process: rundll32.exe
- sysprep.exe launched
So it looks like the cycle of droppers dropping droppers to drop more droppers continues.
We can quickly confirm the activity we saw with Procmon:
Next, we check for the .dll dropped by the malware:
Let's now look for some of the other dropped files:
Lastly, let's look at .pcap for the network activity during our first run of the malware:
Now that we have a general idea of what is going on, let's do some static analysis to confirm this behaviour. I split this into the functionality of the different files to make it a bit easier to read and follow. Let's start with the dropper itself. First, we restore our VM to the last snapshot and start our manual analysis.
Static analysis
Since we already know that the main purpose of the code is to drop several files and launch these, lets see if we can figure out how this is done. Looking at the imports of the binary, we see several imports which might be related:
- ShellExecuteA
- Reg functions
- CreateDirectoryA
- DeleteFileA/W
- FindNextFileA
- CreateFileA/W
Since we know that the first file is created in a new directory let's check for calls to that first.
Sub_405898 Looking at the function we can confirm this, let's rename the function to create_dir.
The call to sub_406EA5 looks like it is used to get the Windows version via GetVersionExA, let's call this “get_version”.
Next in “create_dir” we get a call to sub_4057E8 which we see is used to set file attributes. let's rename this accordingly (“set_file_attr”).
“create_dir” is now analyzed, lets move on to the next, step we saw earlier, the creation of a file for checking access. Let's see where CreateFileA/W is called form. Again looking at the function graph we see the function called from a lot of processes. Let's go through these and see if we can figure out what is going on.
Going through the different functions they are each responsible for creating files with different attributes, none of which are too interesting to us at this point.
Next, let's look for the calls for process creation and command execution, we will do this by checking for calls to “ShellExecuteA”. The first instance of this creates a normal process and also “runas”, likely to create a process as administrator.
After this, we know that the program is checking for access to folders under the %temp% directory. Let's dig further into this behaviour.
We continue this analysis, and after doing this for most of the major functions we have a good idea of how the code works and what is done. Since a lot of the files used by the program are stored under %temp% the next logical step is to check for calls to GetTempPathA a look. Opening the sub shows the following graph:
That is a lot of code, so how would we go about unpacking that? Well, the answer is pretty boring and straightforward, we go through the different execution paths and their sub-routines, and note down what is done:
- Gets current directory and looks through files for .tmp files and deletes those, also performs file moves
- Gets temp path
- Calles CoCreateInstance and also creates a directory
- Execute exe
- Query registry for ProgramFilesDir
Just based on the above, it seems that this function does a lot of the heavy lifting for the main program functionality, let's call it “shellEx_exe_query_reg_get_temp_path” to summarize. This can be further backed up by looking at the references for the function.
Now let's look at those two functions, “sub_40CB33” and “sub_40D344” and we should have a pretty good idea of what the program is doing.
Looking at “sub_40CB33” we see that it is creating some windows with the HTML embedded in the file, likely to appear legit before “shellEx_exe_query_reg_get_temp_path” is called, we will call this “exec_and_show_html”.
“sub_40D344” calls a lot of different APIs but mainly it loads some resources and then calls to create temp files.
Dynamic analysis
Now we have a good idea of the code functionality, let's spin up a debugger to run through the functions to get the next set of files. for this, I simply load the file into x96dbg, place breakpoints on file creation, dir creation, and shellExecute APIs and then just run the application.
When we hit the breakpoint on directory creation we see the temporary dir identified earlier getting created:
Next on CreateFileA, we see the creation of the access check file:
After the access check file, we see that MsMpEng.exe is created in the temp directory:
After that, the MpSvc.dll and readme.txt are also created. After this, the program will execute MsMpEng.exe:
But let's stop execution before the file is executed since we now have the files.
Readme.txt
Well just looking at the file it's pretty obvious that this is some encrypted data. likely a payload for further execution.
Delete.txt
The next file is “delete.txt” which simply contains the full path for the original binary, this is likely used for interprocess communication so that later stages know what binary to delete.
MsMpEng.exe: Dll host
Initial Triage
looking at the strings of the exe we see that the binary interacts with “MsMpEng.pdb” and “mpsvc.dll” these were just created by the last .exe.
Looking at the code of the application we see that it just creates a thread with the .dll.
Giving the imports of the binary a look we see that “ServiceCrtMain” is loaded from mpsvc.dll. We therefore conclude that the purpose of this binary is just to load and execute MpSvc.dll.
MpSvc.dll: Persistence
Initial Triage
Looking at the sections, we quickly see something interesting the .data section contains another .exe file:
Additionally, the .dll has functions related to file creation:
Also interesting is a string reference to a file called “loader.dll”. Unfortunately, I was unable to find any further mention of this file, so it might just have been a mistake left over from previous versions.
Static analysis
Looking through the functions, we see “sub_100012D0” has a reference to “readme.txt” followed by a function call to “sub_1000015A0”, so let's start by looking at those.
This function is particularly interesting as it calls CreateFileW, virtualAlloc, and ReadFile. Potentially saves the file to disk?
The rest of the code does not show anything too interesting, so let's move on to the debugger and see if we can figure out how it goes from readme.txt to file creation and rundll32 (as we saw during initial analysis).
Dynamic analysis
We quickly see the creation of files in different directories:
Inspecting the newly created file shows that it is just a copy of the file from %temp%. Next “delete.txt” is created, this file contains the program used to launch the application, in this case, “x32dbg.exe”. Furthermore, we see a file being created under “C:\ProgramData\Miscrosoft\Crypto\RSA\MachineKeys\sgkey.data” which looks like it contains even more encrypted data.
Worth noticing is that the dropper also goes through and sets the time data for the file to be older, another evasion tactic employed.
Next these two loops go through and decrypt data.
After the loops are done iterating we are left with the decrypted data.
Moving on, more files are created:
- C:\\Users\\IEUser\\AppData\\Local\\Temp\\bpu.ini
- C:\\Users\\IEUser\\AppData\\Local\\Temp\\bpu.dll
Inspecting them we see that bpu.ini looks like a service installation file.
After that we see something interesting a format string “%s:%d” is filled with the location for “pbu” and also sysprep.exe and rundll32.exe. We later find that these processes are the ones responsible for establishing persistence. And that this persistence is then used to run this binary as a service.
These are then used to execute the next stage of execution.
Interestingly the CreateProcessW is not imported directly or with the help of the LoadLibraryA and GetProcAddress libraries. This is probably done to confuse analysts and make analysis more difficult. Running through the program we see what we expected functions getting dynamically resolved:
We now know that MsMpEng.exe creates the “bpu” files and uses rundll32 to run the .dll. Let’s move on and see what these are doing.
bpu.dll
Initial triage
The .dll contains the expected exported function of “bpu” acting as the entry point. It also contains some interesting imports for code execution, persistence, registry modification, etc.
Looking further at the code we see “sub_18000011A0” creates a service, with the strings of the service name being split, likely to avoid detection.
Next, we see functions for starting the service and for requesting the program be run with admin privileges:
Here ShellExecuteExW is also called to execute something which, for now, is encrypted. More files and directories are also created.
After this we get to the most interesting function of the .dll. “sub_1800002560”, this looks to be the main function of the .dll where the following calls are made in order:
The command line of the program is checked -> a file is created, then deleted -> a directory is created -> maybe a file is copied -> a file is created -> the registry key “SOFTWARE\Microsoft\Internet Explorer\Registration” is opened -> more files are created -> “delete.txt” is copied and then a file is deleted -> two services are created and started and finally the process exits.
Now let's throw it in a debugger and see if our assumptions are correct.
Dynamic analysis
After loading the binary into a debugger using the rundll32 technique mentioned earlier. We step through and observe that the binary does what we found from our dynamic analysis, it writes some files and then creates a service for MsMpEng.exe:
After this, the program exits. Below you can see the service created and started:
Comparing the hashes of both we see that the binary here is the same as the one located in %Temp%.
Svchost.exe: C2 agent
We now know that a service is created, for further analysis let’s give svchost.exe a closer look when running the MSMpEng.exe binary. As this is where we see c2 communication coming from and we know from previous analysis that it will contain malicious activity.
C2 Communications
After messing around with debugging for a while I was not getting any closer to figuring out the c2 communications, so I spun up Fakenet-ng to try to see what was getting sent over the network, and after some time finally saw something interesting:
This communication was also observed with ANY.RUN. Let's see if we can extract the code used for these calls and see if we can reverse it. Aside from the expected C2 communication to “thailanddbs.ddns.net” we also see communication to other domains, likely used for backup.
Other domains identified: “ubuntudns.sytes.net”, “debian.servehttp.com”. We also see that this communication happens from svchost.exe, as well as in the next section this is likely because of the use of services for persistence and therefore also c2 communications.
Dynamic analysis
To analyze this part of the malware I spun up fakenet-ng as shown above to get the PID of the instance of svchost.exe running the c2 code then using the PID we can attach a debugger to the process and see what is going on. Placing breakpoints on HTTP-related API calls, we finally see the c2 communications:
Now we can go through the different calls in the binary and have a guess of the general functionality. Below we see an error message for an unknown command (expected with the fakenet-ng response to all requests):
Running the application a bit longer gives us some paths, likely used by the file for temporary storage:
GET request URL being created:
Because this post is getting quite long already, I'm going to stop the analysis here. And maybe one day we will return to something similar to figure out how we can extract code from a debugger and continue our analysis. But for now, we have a good idea of what is going on.
Conclusion
If you have made it this far, thanks for reading. Throughout this blog, we have looked at several ways of conducting analysis and have uncovered some of the weaknesses of online sandbox analysis. Although these are great at giving a mile-high overview of what is happening, they miss a lot of details and are not 100% reliable. As we saw here the sandbox missed several key Indicators Of Compromise (IOCs) that would have been critical for a complete view of the binary.
The only thing that is missing from our analysis is an in-depth analysis of the C2 agent running in the memory of svchost.exe, but as mentioned previously that would probably require a post entirely to itself, so let's shelve that idea for the moment.
Through our analysis, we identified the following capabilities of the malware:
- Uses HTTP for command and control (C2) communications.
- Uses scheduled tasks for persistence.
- Reestablishes persistence between every run, meaning that cleanup would require: Termination of infected processes, deletion of infected files, and removal of the persistence mechanism.
- Lastly: droppers… many droppers.
For the next post we will be doing an easy one, we will be looking at one of the trickbot modules. These are additional capabilities, which can be loaded by the application to perform tasks such as Active Directory reconnaissance etc.
IOCs
File names & MD5 Hashes
- e2dce038ea6a354da4d34d579a02f14c67ceba6a1b4acea59d12101aa1c5585d.exe — 74C293ACDDA0D2C3B5087763DAE27EC6
- %Temp%\RarSFX0\MsMpEng.exe — CC09BB7FDEFC5763CCB3CF7DAE2D76CF
- %Temp%\RarSFX0\readme.txt — 54A9BB1BF4B86867B1F6CC61C0883088
- %Temp%\RarSFX0\MpSvc.dll — 3E32C3F66A7C55514986AE5E5EACAC61
- %Temp%\bpu.dll — 7505562FA0B31D6462153951FDB75B0A
- %Temp%\bpu.ini — D6D00EDDE9FE75F7E6CE3125637B1946
- C:\ProgramData\Microsoft\DeviceSync\MsMpEng.exe — CC09BB7FDEFC5763CCB3CF7DAE2D76CF
- C:\ProgramData\Microsoft\DeviceSync\MpSvc.dll — 3E32C3F66A7C55514986AE5E5EACAC61
- C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\sgkey.data — 474F127731BD59F899C82D0BECA7CF50
Service
- DeviceSync — C:\ProgramData\Microsoft\DeviceSync\MsMpEng.exe — CC09BB7FDEFC5763CCB3CF7DAE2D76CF
Network
GET and POST requests to these domains, with randomized paths.
- httx://ubuntudns.sytes.net
- httx://debian.servehttp.com
- httx://thailandbbs.ddns.net
MITRE
- T1059 — Command and Scripting Interpreter
- T1218.011 — System Binary Proxy Execution: Rundll32
- T1140 — Deobfuscate/Decode Files or Information
- T1564 — Hide Artifacts
- T1036 — Masquerading
- T1104 — Multi-Stage Channels
- T1053 — Scheduled Task/Job
References
- [001] — Malware Analysis: Trickbot, Part 1 — Droppers — https://medium.com/@0x0vid/malware-analysis-trickbot-part-1-droppers-320bf42cc689
- [002] — MalwareBazaar | SHA256 e2dce038ea6a354da4d34d579a02f14c67ceba6a1b4acea59d12101aa1c5585d (TrickBot) (abuse.ch) — https://bazaar.abuse.ch/sample/e2dce038ea6a354da4d34d579a02f14c67ceba6a1b4acea59d12101aa1c5585d/
- [003] — Analysis bookworm_ms.exe (MD5: 74C293ACDDA0D2C3B5087763DAE27EC6) Malicious activity Interactive analysis ANY.RUN — https://app.any.run/tasks/f7f8db81-0e29-4f5e-a30b-fbb7dc5063ba/