Introduction
Rovnix is currently one of the most prevalent 64-bit rootkits, especially since the source code leaked as part of the Carberp malware. Since then, every malware author has basically been able to create their own custom 64-bit rootkit version and we are therefore constantly seeing new variants in the wild. In this blog post, we want so shed some more light on how our system can be utilized to analyze Rovnix samples and how VMRay Analyzer high-level reports look like.
In case you are interested, the hashes of the analyzed sample are as follows:
MD5: 634509289458883269364992f0c267a6
SHA1: 92f2b2733139c6dfb592bca79a6a9fe87da49c66
Overview
Ever since 64-bit Windows operating systems introduced the concept of driver signing, it is no long possible to load arbitrary (rootkit) drivers. In order to still do this, malware authors have equipped their rootkits with certain tricks. There are currently three common ways to achieve this: first of all, malware authors might steal private driver signing keys from legitimate sources or find weaknesses in the author validation process to get their hands on valid driver signatures (for example Uroburos/Turla). Second, they try to exploit vulnerabilities in legitimate signed drivers to get into the kernel and then disable driver signing. Finally, malware can modify the boot sector of the hard disk to load custom code during bootstrapping. Since this code is executed before the operating system takes control, it is possible to suppress the activation of driver signing. As most 64-bit rootkits, Rovnix falls into the third category. Its first action is to overwrite relevant parts of the boot sector and then reboot the system. Since driver signing is disabled by the malicious boot code, the rootkit driver can then be loaded into memory during startup. When the system has booted up completely, the rootkit driver injects userland code into certain processes to perform further malicious operations.
When analyzing the Rovnix sample with VMRay Analyzer, the sample is executed for a certain amount of time (2 minutes in this example). During this analysis, all malicious system activity is monitored and logged. After the timeout, the system is stopped and the monitored data is extracted and transformed into several reports at different abstraction levels. In this blog post we focus on the high-level HTML report, but we also show some excerpts from lower level data representations. The overview page of the high-level report gives a brief summary of the analysis and the utilized analyzer version, as shown in Figure 1. Certain noteworthy events occurred during analysis and are highlighted as red remarks: the boot sector was modified, the system was rebooted, and finally kernel code was executed.
The overview also displays the process graph, which contains one node for each process that has been monitored during the analysis. Typically, this graph is rather small and restricted to those few processes that were created by the malware, or into which code has been injected. In case of kernel rootkits, however, one has to monitor all processes of the system once kernel code is executed. The reason is that (malicious) kernel code may be executed within the context of each running process of the system. The good news is that though all processes are monitored, our innovative monitoring technology restricts the logging output to only those actions which are actually constitute malicious activity. The process graph of the Rovnix analysis looks as follows:
As Figure 2 shows, it is built from two different sub graphs. The first one contains only two nodes, which represent the malware sample that initially installed the rootkit. The second (huge) graph represents the infected system after the reboot and, hence, contains all processes that were started during and after system startup.
Infection
To retrieve more information on the boot sector infection, we take a look at the Behavior subsection of the report. The host behavior of the second process reveals the sample’s main infection procedure; a hidden disk partition is created (containing the malicious driver) and the bootstrap code of the boot sector is modified to gain control at boot time:
For a more detailed view on the infection procedure, we take a look at the function log. This is an XML file that contains all (API) functions, which have been called by malicious code during the analysis. If the prototype of a called function is known, all parameters are extracted and presented in the analysis logs as well. With this, VMRay Analyzer is able to retrieve the parameters of almost all relevant Windows API functions. As such, the function log offers a great opportunity to dive into the details of the malware’s implementation, which goes far beyond the abstraction level of the high-level report. To get more information on a certain high-level activity, one can directly jump to the corresponding position in the function log by clicking the Fn button:
Listing 1 shows an extract of the corresponding part from the Function Log:
As the log shows, first information about the system drive and its partition layout is gathered by reading the drive geometry information and the Master Boot Record (MBR). The Rovnix bootkit is known to create a hidden partition in unused sectors, either at the end of the hard drive or before the first partition if there are enough free sectors. Since the rootkit must read the MBR of the system to find holes for its own custom partition, we see the corresponding read operation in our analysis. VMRay Analyzer is able to dereference the complete buffers of certain APIs (such as file operations) and thus our analysis contains the entire MBR content as seen by the bootkit. For comfortably viewing this content, the Data button to the right of the corresponding read call can be clicked:
In our case, the partition layout shows that the first partition starts at sector 2048 and has a size of 100 MB (Windows 7 default settings). This leaves enough space for the sample to create its hidden partition before the first partition of the system drive. As already shown in Listing 1, the new partition begins at sector 12 and is filled with 41.5 KB of data:
The second step is the (for Rovnix typical) NTFS bootstrap code infection. As shown in Listing 1, the sample loops through the Volume Boot Records (VBR) of each partition to find the NTFS system partition. After this partition is found, the sample injects its own bootstrap code into the existing NTFS bootstrap code. Again, we quickly retrieve the existing code as well as the newly overwritten code by looking at the data dump of the corresponding high-level entry:
At last, the sample forces the system to reboot in order to start the injected bootstrap code and transfer control to the malicious driver.
Boot Initialization
At boot time, the modified bootstrap code ensures that the kernel driver module is loaded into kernel and receives control. Figure 9 shows the first kernel graph of the malicious driver. Kernel graphs are a unique visualization feature of VMRay Analyzer and enable analysts to quickly get an overview of the different malicious code portions and how they interact with each other. Take a look at our previous blog post New Concepts for Kernel Mode Monitoring, if you are interested in the details.
The first Code Block (CB #1/EP #1) of the malicious driver is called from the IopInitializeBuiltinDriver function, which is automatically triggered each time Windows starts up. As shown in Figure 10, the first block registers three different callbacks (notification routines):
- for process creation
- for loading/mapping executable images, and
- for power state change events.
At last, it creates its own driver object (DeviceType=FILE_DEVICE_DISK_FILE_SYSTEM, DeviceName=”Device{2D909AC6-B08F-F7F4-9285-1E01202B0C87}”) with a pseudo random name and starts a new system thread (CB #2/EP #3).
Figure 11 illustrates how this system thread (CB #2/EP #3) reads the MBR of the system drive and gets the device object of the disk driver. Also, it uses IRPs (I/O Request Packet) to communicate with the disk driver (presumably to initialize the hidden encrypted storage). After that, it creates a user mode visible name (“DosDevices{2D909AC6-B08F-F7F4-9285-1E01202B0C87}”) for its own device object (Figure 12) to enable its user mode part to access the hidden storage.
User Mode Injection
Since the driver has registered notification callbacks for process creation, it gets control each time a new process is started. This can be seen in Figure 9: each time a new process is created, CB #6 is called in the context of the (creating) parent process. In nearly all cases this leads to Execution Block EP #7. Only when the explorer process is created from the userinit (parent) process, EP #11 is executed. This already indicates that the malicious driver has a special interest in the explorer process.
As seen before, the driver also had registered a notification callback (CB #5) for the loading/mapping image event. Here again, we can observe the special interest in the explorer process: execution path #6 is executed for all processes except for explorer, where EP #12, #13 to #16, #18 and #23 are executed. Taking a deeper look at the code that has been executed within the notification routines, we see how the driver injects user code into explorer and then executes it. First, in EP #13 a new section object is created and mapped into the lower part of the user memory, and then within EP #16 an APC (Asynchronous Procedure Call) is enqueued to execute this injected code:
The behavior of the injected user mode code is shown in Figure 14: the code tries to download the file “soft64.dll” from its C&C server, which seems to be no longer accessible at the moment of our analysis. As can be seen in the logs, the server at the domain smokejuse.su is down and thus the malicious user mode code stops its execution. The typical behavior from this stage on would be to retrieve commands from the C&C server and then act accordingly.
Conclusion
In this blog post, we gave a brief overview on how complex 64-Bit kernel rootkits can be quickly analyzed with VMRay Analyzer. Especially it’s capabilities to survive reboot, automatically identify kernel code blocks, and visualize them in semantic graphs drastically reduces the time that is needed to dissect sophisticated kernel malware. The essential parts and malicious functionality of each unknown file can be identified in just a few minutes. And all without having to struggle with local infection, evasion, cryptography, or obfuscation.