The generic retro-malware features of ZeroAccess, combined with its advanced rootkit features, makes it one of the most difficult rootkits to deal with, while newer variants of the malware also support 64-bit Windows systems. Peter Ször and Rachit Mathur take a detailed look at the rootkit.
Copyright © 2011 Virus Bulletin
The ZeroAccess rootkit first appeared in 2009, during the early heyday of the TDSS (TDL2) rootkit . ZeroAccess takes its name from a leftover path to its debug file, but the threat is also known as ‘max++’ due to the fact that it uses this string in one of its device object names. Its likely origin is China, but this is only a guess based on the fact that the rootkit’s command and control (C&C) servers all point to ‘.cn’ domains. The names of these domains are generated semi-randomly based on the date of the system – borrowing the trick from Conficker, which was the first to use it.
ZeroAccess has a lot of similarities with TDSS. In particular, both of them attack a randomly selected device driver, both use areas of the disk outside of the regular file system (depending on variants), and both utilize RC4 encrypted disk volumes. Newer versions of ZeroAccess hide encrypted files inside a folder that has a very similar name to those normally used by Windows Update during patch delivery.
This folder would be something like C:\Windows\$NtUninstallKBnnnnn$, where the ‘n’s are randomized in each system. In addition, newer variants use a twisted RC4 algorithm which also ensures that the encryption key is unique to each system. The rootkit monitors access to this location, and encryption/decryption will happen on access, as needed.
While TDSS parasitically modifies driver files, ZeroAccess replaces its victim driver files and shows a fake clean copy of these files to the AV and security products later on. It does so based on a below-disk-level hook, using a very unconventional technique in which it gains access to the device extension structure of the driver disk’s \Device\Harddisk0\DR0 device, and manipulates it with the LowerDeviceObject field. After that, it can filter IOCTL messages passed down to the SCSI driver, below disk. This is an important feature because it means that ZeroAccess can prevent direct NTFS access from reaching its malicious code on the disk (unless the rootkit is first cleaned from memory, of course). To add to the mix, this level of infection also makes the cleaning of the rootkit more difficult, as NTFS caches the disk, which can easily interfere with the cleaning logic.
In order to fight back against memory scanning, recent variants of the ZeroAccess rootkit utilize another novel technique. This heuristic technique is very generic and able to identify most security products and rootkit detectors, as well as utilities that could be used to discover the rootkit’s presence.
ZeroAccess employs a very poorly documented feature of the Windows kernel by scheduling a user-mode APC (Asynchronous Procedure Call) from kernel mode. Since APCs are executed on behalf of a target thread belonging to a particular target process, the malicious code will seemingly appear as part of the security product itself. However, the scheduled routine will force an ExitProcess() API to be called within the target process, thus forcing a Harakiri. The target process will dutifully execute the request and terminate itself, using its very own thread. In addition, similarly to Pinkslipbot, ZeroAccess will also manipulate the ACLs of the target process corresponding to the executable in question. Upon another ‘execution’, the program will fail to load, as the user no longer has the rights for this action (until the ACLs are restored). We can only hope that, while this technique is highly effective, the result is somewhat counterproductive as users are likely to notice the dying security products and tools on their system. Since ZeroAccess kills most AV products and tools, we decided to take a closer look at its sniper feature.
The heuristics against AV and security products make use of several ‘flypapers’, or lures, to catch them. In newer variants, the first such lure is a rootkit device handle, with a name such as ACPI#PNP0303#2&da1a3ff&0. If this device name is opened without a full path to it (as opposed to directly manipulating it in memory), the rootkit will notice the action. If a product starts up and queries device names, it will quickly be identified as a possible AV scanner or rootkit detector.
As another lure, the rootkit will create a goat process and if the goat process is opened for access, it will take action. This goat process is typically somewhere in the Windows folder, and runs from an alternate data stream. The file is named using random numbers, and within it, there is a short executable in an alternate data stream that is also named with random numbers. This is the actual goat program. Earlier variants (seen in June and July this year) created a device named svchost.exe and used a goat process also named svchost.exe whose path would be: globalroot\Device\svchost.exe\svchost.exe.
Another similar technique used by ZeroAccess is to check access to particular files on disk. By hooking the lower level I/O, the rootkit is capable not only of monitoring access to its own files and faking their content, but also of punishing any access by unwanted processes which execute a thread related to the I/O in question.
Interestingly, in the case of goat processes, the goat process remains visible while the rootkit is active. While this appears rather suspicious, it needs to be visible to attract as many security tools as possible. We should certainly make note of this for an anti-memory scanning 101. Sadly, it is a lot easier to detect AV products than Fake AV programs.
During our initial analysis we were surprised to find that some tools could be used to open the rootkit’s goat file, while others could not, and quickly got killed. This is thanks to an exception built into the rootkit logic, which will decide not to take action if the contender’s PE file header information contains 5.1 as the major and minor OS versions. In such a case, the access will be unimpeded, and no action is taken by the rootkit to prevent the tool’s usage. This explains why one can open the malicious stream using Notepad when another useful utility, HVIEW.EXE, will quickly be punished for attempting to do the same. It was pleasing to bring back GMER and other useful tools by patching their file headers. This exception probably exists in the rootkit to prevent the killing of OS-related processes which could occasionally access the rootkit’s goat data stream. It could also help with the updating of the rootkit – not to mention its cleaning.
An additional check also verifies whether the PE file header has a certain time date stamp value (0x4E3E82AE) followed by a checksum field containing 0x5440. If this is the case, the killing action will also be omitted.
It is worth mentioning that the killing action requires some preconditions to be fulfilled. Most importantly, the thread that accesses the malicious ‘flypapers’ will need to be in a certain wait or alertable state, and must hang around long enough for the malicious APCs to be scheduled in their context. If, for example, the thread quits quickly enough, it cannot be killed (at least not via the APC routine).
An asynchronous procedure call (APC) is a function that executes asynchronously in the context of a particular thread. When an APC is queued to a thread, the system issues a software interrupt. The next time the thread is scheduled, it will run the APC function if the right conditions are met. The APCs are delivered by KiDeliverApc() of the kernel . During these acrobatics, if a user-mode APC is scheduled, the kernel will save the context of the actual thread to the stack. This will be picked up by NTDLL’s ZwContinue() to restore the thread’s context after the kernel’s ‘hijack’ and execution of the APC routine have occurred. This happens within the undocumented function of NTDLL, called KiUserApcDispatcher(), which will first pop the APC routine’s address from the stack, placed there by the earlier calls from kernel, and then use CALL EAX to execute the APC routine (Figure 1).
When the kernel component of ZeroAccess intercepts access to one of its flypapers, it allocates a page by calling the NtAllocateVirtualMemory() API. This page is 4KB long and is allocated in the user process address space of the current (security scanner) process, with executable, writeable rights (Figure 2).
This malicious page will then be filled with the APC function to look for kernel32.dll’s ExitProcess() API reference in the process address space and to execute it in the context of the thread of the application. Each thread has its own APC queue. ZeroAccess queues an APC to a victim thread by first calling the KeInitializeApc() kernel function, followed by KeInsertQueueApc(). For KeInitializeApc() it specifies the NormalRoutine parameter as the address of the new page that contains the code for the malicious APC, and the ApcMode parameter as 1. This means that it will be a user-mode APC.
After that, once the user-mode APC is invoked, the CALL EAX instruction in KiUserApcDispatcher() from Figure 1 calls this malicious APC function, as shown in Figure 3. The function first locates the kernel32 base address by enumerating the loaded module list in the PEB loader data structure. It then looks for the ExitProcess() API in kernel32 exports and calls it. This leads to the quick termination of the security scanner process. Since the address (0x7c90eac7) of the instruction following the call EAX remains on the stack, this address becomes the ExitCode parameter provided to ExitProcess().
The Windows kernel uses the APC mechanism intensively to satisfy the needs of important Win32 APIs. In fact, in 2008, a Chinese application called killme.exe appeared. This little application was a demonstration of how difficult it could be to kill a process. Killme has four different tricks to prevent its termination. One of these is related to the manipulation of the KernelApcDisabled variable of the KTREAD structure of killme.exe’s thread to ensure that certain APIs related to process, thread discovery and termination would be forced to fail. This is due to the fact that the kernel’s KiInsertQueueApc() function checks for the KernelApcDisabled flag before inserting an APC to the queue of the target thread, and such functionality is needed to execute certain Win32 APIs properly. Killme.exe used a kernel-mode driver for this manipulation.
Evidently, the generic retro-malware features of ZeroAccess, combined with its advanced rootkit features, makes it one of the most difficult rootkits to deal with. In addition, newer variants of ZeroAccess also support 64-bit Windows systems, just as TDSS does.
In fact, the malware’s retro features might be so strong as to not only fight back against AV and security tools, but also to function as a self defence mechanism against other rootkits to keep compromised machines under its control for longer periods of time. TDSS is notorious for going after competitor rootkits.
The authors of TDSS and ZeroAccess also use similar infection vectors, such as rootkit installers as fake cracker applications distributed on the same sites, and even use similar drive-by-download exploitation techniques to hit new targets. Victims typically find that their Google searches take them to some advertising sites (and money is made in the process for the attackers).
Unfortunately, the advantage of security products only dealing with threats in user-mode is long gone. Increasingly, we can expect threats to appear in kernel land, as ‘hash-busted’ masses of hundreds of thousands of rootkit variants clearly represent an ever-growing threat. Such novel kernel exploitation techniques and kernel-mode attacks against AV are also likely to increase as a result. We need to raise the bar, yet again!