Malware Analysis: Trickbot, Part 2 — Agent

0x0vid
14 min readJul 2, 2024

--

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.

ANY.RUN analysis

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

procDOT — sorry for the bad quality

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:

  1. Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\__tmp_rar_sfx_access_check_1933125
  2. Delete file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\__tmp_rar_sfx_access_check_1933125
  3. Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\MsMpEng.exe
  4. Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\MpSvc.dll
  5. Create file: C:\Users\IEUser\AppData\Local\Temp\RarSFX0\readme.txt
  6. Create process: MsMpEng.exe
  7. C:\Users\IEUser\AppData\Local\Temp\RarSFX0\MpSvc.dll loads thread 4852
  8. thread 4852 reads C:\Users\IEUser\AppData\Local\Temp\RarSFX0\readme.txt
  9. thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\MsMpEng.exe
  10. thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\MpSvc.dll
  11. thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\delete.txt
  12. thread 4852 creates file: C:\ProgramData\Microsoft\RSA\MachineKeys\sgkey.data
  13. thread 4852 creates file: C:\ProgramData\Microsoft\DeviceSync\MpSvc
  14. thread 4852 creates C:\Users\IEUser\AppData\Local\Temp\bpu.ini
  15. thread 4852 creates C:\Users\IEUser\AppData\Local\Temp\bpu.dll
  16. thread 4852 creates process: rundll32.exe
  17. 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:

Process tree for initial executable

Next, we check for the .dll dropped by the malware:

.dll and .ini dropped by initial malware

Let's now look for some of the other dropped files:

Additional files dropped by the malware

Lastly, let's look at .pcap for the network activity during our first run of the malware:

Network communications

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.

CreateDirectoryA function graph

Sub_405898 Looking at the function we can confirm this, let's rename the function to create_dir.

CreateDirectory function

The call to sub_406EA5 looks like it is used to get the Windows version via GetVersionExA, let's call this “get_version”.

Get versions function

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”).

Set file attributes function

“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.

CreateFileW function graph

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.

ShellExecute calls

After this, we know that the program is checking for access to folders under the %temp% directory. Let's dig further into this behaviour.

Temp file creation

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:

GetTempPathA function 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:

  1. Gets current directory and looks through files for .tmp files and deletes those, also performs file moves
  2. Gets temp path
  3. Calles CoCreateInstance and also creates a directory
  4. Execute exe
  5. 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.

Calls to the execution with the temp files

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:

%temp% directory creation

Next on CreateFileA, we see the creation of the access check file:

rar_sfx temporary file creation

After the access check file, we see that MsMpEng.exe is created in the temp directory:

Creation of MsMpEng.exe in %temp%

After that, the MpSvc.dll and readme.txt are also created. After this, the program will execute MsMpEng.exe:

Execution of 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.

Encrypted file contents

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.

Contents of delete.txt

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.

MsMpEng imports

Looking at the code of the application we see that it just creates a thread with the .dll.

Main function

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:

Embedded PE file

Additionally, the .dll has functions related to file creation:

Imported functions

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.

Loader.dll

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.

readme.txt reference

This function is particularly interesting as it calls CreateFileW, virtualAlloc, and ReadFile. Potentially saves the file to disk?

Read file

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:

Create file

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.

sgkey.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.

Decryption loops

After the loops are done iterating we are left with the decrypted data.

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.

bpu.ini

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.

Interesting format string

These are then used to execute the next stage of execution.

format string for rundll32 command

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:

LoadLibraryA and GetProcAddress

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.

Service creation strings

Next, we see functions for starting the service and for requesting the program be run with admin privileges:

Runas administrator

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:

Rundll32 with this .dll

After this, the program exits. Below you can see the service created and started:

Autoruns showing the service created for persistence

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:

C2 communication captured using Fakenet-ng

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.

More intercepted c2 communication

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:

POST message

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):

C2 agent error message

Running the application a bit longer gives us some paths, likely used by the file for temporary storage:

Suspect file paths.

GET request URL being created:

GET request URL path

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

--

--