007 - Decoding XWorm: Initial Exploration and Anti-Analysis Techniques
Este artículo también está disponible en español
- Introduction
- Initial Exploration and Anti-Analysis Techniques
- Coming Soon: Defense Evasion and Persistence
- Coming Soon: Lateral Movement
- Coming Soon: Keylogger and Cryptocurrency Hijacking
- Coming Soon: Communication with Telegram, Retrieving a New Variant
- Coming Soon: Command and Control
If you want to be notified when new posts in this series are published, don’t forget to subscribe to the blog!
1. Introduction
In the previous article we identified that the RAT was built using the .NET framework, which makes it easier to analyze due to the wealth of information contained in binaries created with this framework. In this article, we will delve into the techniques XWorm uses to hinder analysis.
2. Initial Exploration
After opening XWorm in DNSpy, we verify that the names of classes and objects are obfuscated:
It would be tedious to review each class to identify the function where the program starts execution; fortunately, DNSpy displays the entry class and function as a comment:
By clicking on the function name, DNSpy takes us directly to it, where we can see the code that gets executed when the malware starts:
Once execution begins, the malware calls Thread.Sleep, passing as parameters the value stored on the variable sWpIi59HVTjtB0r6P7SRQdLwgcnM2a0ZVHXvX multiplied by 1000. By clicking on the variable, we verify that it has a value of 2, so the value passed to Thread.Sleep is 2000. One of the advantages of analyzing .NET code with DNSpy is that it contains information about the various functions and native methods of the languages that use this framework. When hovering over the function, we can see that it is used to pause the thread for N milliseconds. Since the value passed to the function is 2000, the program halts for 2 seconds.
After sleeping for 2 seconds, the malware enters a try/catch block, where it attempts to execute several operations. If executing these operations fails, the program closes using the Environment.Exit(0) function.
At first glance, it may seem a bit odd what the malware does, as it assigns the result of an operation on a variable to the variable itself:
Class.variable1 = Conversions.ToString(lnZZgsJ1tVOV.FbmCgvom7sJS(Class.variable1));
If we click on the first variable, qsurotxVBQWuN1wXL7Sl3R7UMOoGherwjkt90, we see that its value is nz4SABi5PYTEufPjSTbCd8mMnZZi6YWaGiwAg1FVXfo=
The value seems to be encoded in Base64; however, attempting to decode it does not yield readable characters:
3. Runtime decryption
If we click on the FbmCgvom7sJS function, we can understand what operations it performs on the variable:
public static object FbmCgvom7sJS(string TEFe4AuGLs1t)
{
RijndaelManaged rijndaelManaged = new RijndaelManaged();
MD5CryptoServiceProvider md5CryptoServiceProvider = new MD5CryptoServiceProvider();
byte[] array = new byte[32];
byte[] array2 = md5CryptoServiceProvider.ComputeHash(TFIW2FSLtw9S.fOEct6S2qWNI(Dwre7AimAttsSDe9ONtyGoMXtbA3NNJR6lGec.eCx5LqBibLns0nMQEXWWSiIdLt37W7nhFgXiM));
Array.Copy(array2, 0, array, 0, 16);
Array.Copy(array2, 0, array, 15, 16);
rijndaelManaged.Key = array;
rijndaelManaged.Mode = CipherMode.ECB;
ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor();
byte[] array3 = Convert.FromBase64String(TEFe4AuGLs1t);
return TFIW2FSLtw9S.kX1tPkTzXln3(cryptoTransform.TransformFinalBlock(array3, 0, array3.Length));
}
The function performs the following steps:
- It initializes an instance of RijndaelManaged, a class used in cryptographic operations.
- It initializes an instance of MD5CryptoServiceProvider, which allows for performing MD5 hashing operations.
- It creates an array called array (1) of 32 bytes.
- It creates another array called array2 (2) that contains the MD5 hash of the value stored on the variable eCx5LqBibLns0nMQEXWWSiIdLt37W7nhFgXiM.
- It copies the MD5 hash from array2 (2) into the first 16 bytes of array (1).
- It copies the MD5 hash from array2 (2) again into the first array (1), starting at the 15th byte; by doing this, the 15th byte, which was the last one copied in step 5, is overwritten.
- It configures the 32-byte array (1), which now contains the duplicated MD5 hash, as the key for the cryptographic algorithm and initializes the method used to decrypt strings.
- It obtains the value passed as a parameter to the function and decodes it from Base64.
- Finally, it decrypts the byte array resulting from the previous step using the key set in step 7.
By analyzing the function, we can finally understand its purpose: the value of certain variables are encrypted and the malware decrypts them during execution, possibly to prevent values such as URLs or IPs from being easily identified as Indicators of Compromise (IOCs) in static analysis.
While we could create a script to automate the decryption, we can use DNSpy’s debugging capabilities to obtain the decrypted strings. We can press F9 on the line where the first string is decrypted and analyze the result, and F10 to step through each line:
From the decrypted strings, we identify some that may be potentially interesting:
- A DDNS.net URL, which is a dynamic DNS service.
- What appears to be a port.
- The string “<Xwormmm>”
- The string “USB.EXE”
Some of the decrypted strings don’t make sense yet, and we will probably have to infer their usage based on the context of the function that invokes them.
4. Mutex usage
After decrypting the values, the malware enters the function XykaLtFvQmKZ, which attempts to create a Mutex with a specific name, which is stored in the variable eCx5LqBibLns0nMQEXWWSiIdLt37W7nhFgXiM:
Mutexes are commonly used in concurrent programming to lock certain parts of the code and prevent them from being accessed while in use or not yet ready. This happens because, when managing multiple threads, a race condition can occur, where one thread finishes before the thread we expected to finish first.
The parameters to create a Mutex are the following:
- initiallyOwned: It does not affect the malware’s objective.
- name: name of the Mutex.
- createdNew: True if the Mutex got created, False if a Mutex with the requested name already existed.
In this case the malware is running in a single thread, but the malware authors creatively use this type of object by looking at the createdNew response parameter. This way, if a new instance of the malware is started while another is already running, the Mutex with the name defined by the malware will already be created, and the result would be False, causing the program to close. By doing this, the malware authors can ensure that only one copy is running at a time, preventing system performance impact and avoiding suspicion.
5. Anti-analysis
After ensuring that it is the only instance running, the malware enters the function w8r25j4la24nAJZBLOLGewTPs69UXozPFVUsT. By reviewing the function at a high level, we can see that it performs multiple validations and proceeds to close itself if any of these fail:
Going through each function, the malware performs the following:
-
It checks if it is running in VMWare or VirtualBox: To do this, the malware retrieves the system’s manufacturer using Select * from Win32_ComputerSystem and compares it with the strings ‘vmware’ and ‘VirtualBox’
-
Checks if its being debugged:
The malware imports the CheckRemoteDebuggerPresent function from the Kernel32.dll library to verify if the current process is being debugged by an external debugger.
- It checks if the SbieDll.dll DLL is loaded:
The library SbieDll.dll belongs to Sanboxie, a program that allows sandboxing and analyzing programs. XWorm uses the GetModuleHandle function to check if the library is loaded.
-
It checks if it is running on Windows XP:
-
It checks if it is running on a cloud provider: The hosting parameter from the ip-api.com website indicates whether the IP belongs to a cloud provider.
These validations can serve different purposes:
- The check to see if the environment is virtualized, that the malware is not running on XP, and that it’s not running on a cloud provider could be due to the malware’s capabilities, which may require physical hardware.
- The check to see if it’s running on a cloud provider might be to prevent analysis by solutions like VirusTotal or AnyRun, which operate on cloud services (AWS/GCP/Azure).
- The check to see if it’s being analyzed and if the Sandboxie library is present may be intended to hinder detection by dynamic analysis tools.
If we were to continue with dynamic analysis on a PC with VMWare or VirtualBox, the program would close due to the first check:
To avoid this, we can set a breakpoint before the manufacturer is validated and change its value in memory:
It is also possible to save the program after patching the validation; however, I do not recommend this until fully understanding what the malware does. The malware could proceed to corrupt files or encrypt the system, so it’s better to keep its initial function and allow it to close if we were to run it by mistake or in case it starts automatically using persistence techniques.
4. Conclusions
In this analysis, we have delved into the initial execution stages of XWorm, uncovering several techniques it uses to hinder analysis and ensure its execution in specific environments. Some of the key points include:
Obfuscation and Dynamic Decryption
XWorm uses Rijndael-based encryption and Base64 encoding to protect sensitive strings such as URLs, ports, and filenames. This approach aims to hide potential Indicators of Compromise (IOCs) until they are executed in memory.
Mutex usage
The malware ensures that only one instance is running by creating a unique Mutex. This prevents raising suspicion due to excessive system consumption.
Anti-Analysis Validations
XWorm’s multiple checks, such as detecting virtualized environments, external debuggers, and the presence of analysis libraries like Sandboxie, make dynamic analysis more difficult and aim to prevent execution in controlled environments.
These techniques not only show a high level of sophistication in the development of XWorm but also highlight the importance of advanced tools and methodologies in malware analysis. By overcoming these barriers, we can understand the behavior of such threats, anticipate their movements, and develop better defenses.
Next Steps
In the next articles of the series, we will explore XWorm’s defense evasion strategies, persistence techniques, and functional modules such as a keylogger and cryptocurrency mining capabilities.
See you in the next article!
Do you have any comments or suggestions? You can leave your feedback in the form below!