Rhadamanthys malware analysis: How infostealers use VMs to avoid analysis
The infostealer malware Rhadamanthys was discovered in the last quarter of 2022. Its capabilities showed a special interest in crypto currency wallets, targeting both wallet clients installed in the victim’s machine and browser extensions. The main distribution methods observed for this threat are fake software websites promoted through Google Ads, and phishing emails, without discriminating by region or vertical.
While its information stealing capabilities and distribution mechanisms have been seen before, it is its downloader component that truly stands out. By mixing advanced anti-analysis techniques coupled with heavy obfuscation, it makes analysis by traditional security methods incredibly difficult.
The Rhadamanthys downloader is mainly coded in C++ and features a staged execution that makes use of a variety of anti-analysis techniques:
- Virtual machine (VM) obfuscator.
- Heavy VM and sandbox detection capabilities. Both custom and imported from the open-source tool al-khaser.
- Embedded file system that employs custom file formats.
Rhadamanthys’ anti-analysis features haven’t gone unnoticed. Zscaler investigators found that the VM obfuscator used is the Quake 3 VM. Furthermore, the analysts related those custom file formats to the ones used by the crypto miner Hidden Bee. Recently, CheckPoint published an in-depth analysis of the inner workings of the virtual filesystem.
In this post we take a look at what Rhadamanthys developers are using the Quake VM for, which modifications have been made to it, and how the virtualized code has evolved through the different versions of the malware. We have also published IDA Pro modules for disassembling standard and Rhadamanthys’ QVM binaries which you can find in our public GitHub repository.
Devirtualizing Quake VM
As a very brief introduction to the concept, VM obfuscators aim to hide code by implementing a custom processor, with its own set of instructions and an interpreter that translates custom instructions into native code. There is plenty of documentation on the fundamentals and components of this obfuscation mechanism, such as this article by Tim Blazytko.
To study the Rhadamanthys downloader it is necessary to analyze the Quake 3 VM. Fortunately, there is great documentation on its inners and an open-source version was published on GitHub.
This kind of open-source project can be attractive for malware developers willing to use VM obfuscators as they provide widely tested components. The task of implementing a custom virtual processor is not trivial and can lead to bugs of all sorts and/or a considerable loss of performance if the developer lacks experience and solid knowledge on the matter.
After locating the Q3VM interpreter within Rhadamanthys’ first stage and extracting the embedded code it processes, we decided to make a disassembler to take a look at its features.
Early versions of Rhadamanthys loader
Within the malware samples distributed during the first months of 2023 it is possible to spot the Q3VM interpreter loop as it is implemented in the original Github repository. The main changes the malware developers made to the VM are found on the syscalls. Within the Q3VM project, and for the rest of this article, the term syscall is used to refer to functions implemented in native code, that are available to the virtualized code and allow it to interact with the native ecosystem, for example, calling functions from Windows dlls or transferring memory from the VM space to the native program.
These calls can be spotted within QVM files by searching for CALL instructions with a negative argument. Originally, the syscalls of the Q3VM project are 4, and they allow to call the functions printf, fprintf, memset and memcpy from the virtualized code.
However, this version of the malware contains a total of 12 syscalls that replace the original ones. Apart from providing access to the memset and memcpy functions, the modified syscalls allow the virtualized code to interact with key components of the native program, enabling to read the memory space of kernel32.dll and resolving its exports, as well as providing some utilities to decode and transfer strings from the embedded QVM file to the main program.
Once reviewed the basic features of the Quake VM we can now discuss the inners of Rhadamanthys’ obfuscated code. For this version of the malware the code has 4 different paths that will be picked depending on the first argument received by the VM:
- 0: Decode shellcode received as an argument.
- 1: Resolve VirtualProtect.
- 2: Use VirtualProtect to set execution permissions to shellcode.
- 3: Transfer and decode to the main program the strings ‘Avast’ and ‘snxhk. Rhadamanthys’ loader will check for the presence of Avast AV before executing its next stage.
Rhadamanthys 4.5 and above
With the introduction of version 4.5 and the posterior versions, the VM component of the loader has received substantial changes. The logic of the virtualized code has been reworked, and thus syscalls have been modified.
On the native side, the number of syscalls has been reduced although their main functionality is still the same. Providing means to access the memory space of loaded modules and transferring data between the virtual and the native environments.
The interpreter of the VM has also been updated, the opcodes of the instructions have been modified in an aim to prevent the identification of the VM and its disassembly. Within the embedded QVM file it is possible to observe a new logic. The new operations, depending on the argument passed from the native program are:
- 0: Resolve an export of ntdll. Hashes are no longer hardcoded within the QVM file but passed as an argument. The ROR13 encoding prevails.
- 1: Not implemented.
- 2: Resolve kernel32 function.
- 3: Decrypt stage 2 using an algorithm of the TEA family,
New module heur.bin
Apart from rebuilding the VM components of the stage 1, Rhadamanthys developers have recently added a new module with anti-VM capabilities that will execute before al-khaser‘s checks during the execution of the decrypted shellcode.
Internally the module contains 3 methods to detect virtualized environments, all of them involving the cpuid instruction.
The first check compares the time the victim machine takes to execute the cpuid instruction against the performance of the fyl2xp1 instruction. Due to how VMs need to handle the execution of the cpuid instruction, it takes longer to execute it in such environments than it would take in a bare metal machine. For a more in-depth explanation of this behavior, you can check the talk My Ticks Don’t Lie New Timing Attacks for Hypervisor Detection by Daniele Cono.
The other 2 checks are very similar and use the cpuid instruction with the parameter 0x40000000, which should return no results on physical machines. Then compares it with the result of calling cpuid with an invalid input. If either of the results are not 0, heur.bin assumes it is being executed in a VM.
The implementation described serves two purposes, not only detecting the presence of a hypervisor but also possible manipulations done to the output of cpuid in an attempt to harden analysis tools against these detection techniques.
Disassembling standard and Rhadamanthys’ Quake VM binaries
Although Rhadamanthys’ activity has fallen since it reached its peak at the beginning of the year, it is still receiving significant updates. The constant addition of new anti-analysis features and the upgrading of the existing ones, as well as their complexity, shows the maturity of the threat actor behind its development.
However, often obfuscation is a double-edged sword, as very specific protections can lead to very accurate detection means, thus the importance of thorough analysis. To ease the task, you can find the source code of the QVM processor and loader modules for IDA Pro in our GitHub repository.