Threat Advisory: XWorm, Part 2 – Breaking Down the .NET Loader and v4.0

Aaron Goldstein
Brent Murphy
June 22, 2023

This is Part 2 of Todyl’s breakdown of XWorm 4. Click here for Part 1.

In the first part, we detailed the four files used to propagate the .NET loader in this XWorm attack. Now, we’ll dig deeper into the .NET loader as well as the XWorm malware itself.

It’s important to note the fact that XWorm is a continuously developed product and a rapidly evolving threat. So, although this information is accurate as of the time of writing, the community will need to remain vigilant to ensure no one else falls victim to it as it evolves. That said, let’s get into it. If you are interested in the associated YARA rules, you can find them at the bottom of the post.

TL;DR: XWorm v4.0 has some key differences from previous versions, namely that it is a modular malware that can dynamically load plugins onto the machine, as well as monitor system activities and execute on the disk, memory, and shell levels.

.NET loaders 1 (2.7) and 2 (4.0)

As we said in Part 1, XWorm has changed since the last few times it has surfaced. Here are a few of the key differences we’ve seen between then and now.

First .NET loader (2.7)

This .NET assembly, which was called in the .ps1 file in Part 1, seems to act as the primary Loader for the follow-on XClient RAT (remote access trojan).  

Like the one mentioned by Elastic, this .NET loader utilizes multiple obfuscation techniques such as dead instructions, renamed symbols, and string manipulation to hide its actions. It seems that they employed slightly different string manipulation techniques, as well as a Debugger Detection mechanism to further cause issues with reversing the binary. Additionally, we were able to identify a hardcoded AES encryptor/decryptor that is utilized to read/write files to disk. Here are some key features of 2.7:

  • Function Creation
  • Debugger Detection
  • AES Encrypt/Decryptor

Windows API function loading

Figure 1
Figure 2

Adversaries can load the functionality of both LoadLibrary and GetProcAddress. This allows them to leverage external functionality not originally present in their code. By using LoadLibrary, adversaries can load malicious code or legitimate system libraries to perform various actions, such as executing arbitrary commands or accessing sensitive resources. GetProcAddress enables them to retrieve the addresses of specific functions within loaded libraries, enabling precise control and manipulation of those functions. This step uses a similar version of string manipulation to deobfuscate the function calls that will be utilized for loading the secondary .NET (XClient 4.0).


Figure 3
Figure 4
Figure 5

This utilizes the ECB method of AES Encryption/Decryption. We have seen indicators of this being utilized for reading/writing files from a Binary Stream but haven't been able to acquire any artifacts of the files that were being utilized.

Debugger check

Figure 6

By incorporating debugger checks, they can employ anti-debugging techniques, alter their code behavior, or terminate execution to hinder analysis and make it more challenging to understand their malicious intentions.

Comparing the Second .NET (Xclient 4.0) to v3.1

Compared to the findings on 3.1 from SonicWall and Elastic, we’ve found several differences in the second .NET loader, Xclient 4.0.


A key difference that we have seen with v4.0 is the incorporation of modular malware. There has been key functionality added to XClient[.]exe. We have not seen any plugins being loaded into the framework, so we are unable to show specific plugin functionality. We can, however, break down the process with which they load, run, and persist plugins that are loaded into the framework.

Plugin load

Plugins are loaded in 1 of 2 ways.

  1. New plugins are sent to the endpoint socket and are both AES encrypted as well as compressed via a custom compression function.
  2. Existing saved plugins are loaded from registry. These are compressed via a custom compression function.

Plugin execution

Once loaded the plugins are loaded into the memory space of XClient, this shows that these plugins are .NET assemblies. Once loaded, they have a variety of explicitly defined methods to be utilized. We don't have access to a plugin, so we are unable to show the exact functionality, but here are the method names used:

Figure 7

Plugin persistence

Plugins can be persisted via the "savePlugin" command. This command will take the contents of the plugin and write them to the registry, the registry path will be "Software\\<Hash Value>". The Hash Value is a custom hash created by multiple environmental variables hashed together.

Figure 8
Figure 9

Once the registry key is created, it then utilizes a custom compression method to write the .NET assembly plugin to the registry key. If you are interested in learning more about the custom methods used, please reach out to us to discuss.


Another difference we’ve found is how 4.0 monitors running processes and sends a message whenever a process with a matching MainWindowTitle (the title of the main window of an application) is found.

In C#, the code initializes a list called "source" which is associated with a placeholder called "a0". This allows for the input of specific process string values. The input data string is then split using commas as separators, and the resulting substrings are stored in the "strArray" string array.

Figure 11

When the ClientSocket has an active connection, adversaries (potentially malicious actors) can utilize a specified list to check for processes on the host.

The code also uses a timing mechanism within a while loop. It continuously checks for processes within a certain time interval specified by the timer. During the specified time interval, any observed processes that match the specified criteria are sent as messages over the connection socket. This allows the program to communicate this information remotely.

This code can be utilized for monitoring processes or applications that are currently running on a system. For example, it can be used to detect the presence of debugging tools or sandboxing tools, indicating that the system may be under analysis or testing. Upon detecting such tools, the adversaries can take action, such as terminating the live socket connection to the targeted device, potentially to evade detection or prevent analysis.

Disk, memory, and shell execution

With the newest XWorm, the adversaries behind it have expanded their attack surface, allowing a variety of execution methods to be performed on the endpoint.

Disk execution

4.0 allows adversaries to send PowerShell scripts, as well as other scripting languages over the network to the remote endpoint to be executed and saved to disk.

Figure 12

Memory execution

4.0 provides the ability to send .NET assemblies over the network to be executed in the memory space of the XClient assembly.

Figure 13

Shell execution

4.0 send commands to remotely run in the context of the XClient assembly.

Figure 14

Other differences

Figure 15

Indicators of Compromise

The .NET Assembly names are:

  • 2.7.dll (Loader) - 65e47578274d16be1be0f50767bad0af16930df43556dd23d7ad5e4adc2bcbe3
  • XClient.exe (Framework) - 7e721f5222fa4d3e95c1bd96c5976d9ea55451cd2b03340d4d1d2b1062d13079
Figure 17

YARA Rules

rule XWorm4

      $string1 = "XWorm V4.0"
      $string2 = "70879587-716b-4c90-9c72-2fc089fabf14"
      $string3 = "-ExecutionPolicy Bypass Add-MpPreference -ExclusionPath"
      $string4 = "-ExecutionPolicy Bypass Add-MpPreference -ExclusionProcess"
      $string5 = "OfflineKeylogger Not Enabled"
      $string6 = "\r\nConnection: keep-alive\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent:"
      $string7 = "\r\nContent-length: 5235\r\n\r\n"
      any of them

rule Xworm_Loader
      $signature1 = { 58 57 6f 72 6d 20 56 34 2e 30 0d 0a 37 30 38 37 39 35 38 37 2d 37 31 36 62 2d 34 63 39 30 2d 39 63 37 32 2d 32 66 63 30 38 39 66 61 62 66 31 34 0d 0a 2d 45 78 65 63 75 74 69 6f 6e 50 6f 6c 69 63 79 20 42 79 70 61 73 73 20 41 64 64 2d 4d 70 50 72 65 66 65 72 65 6e 63 65 20 2d 45 78 63 6c 75 73 69 6f 6e 50 61 74 68 }
      $signature2 = { FC DF B1 38 5C 59 D1 F7 EC 12 C6 07 BF 79 1C CB }
      any of them

Learn more

We will continue to research and better understand XWorm v4.0 as time goes on, so be sure to stay tuned to our blog for all the latest updates.

Stay up to date

Subscribe to receive the latest insights, news, and updates from Todyl.

Additional reading

Todyl updates

Sign-up to get the latest from Todyl sent straight to your inbox.