Black Hat USA 2020 Highlights: Demystifying Modern Windows Rootkits

 

#3 Demystifying Modern Windows Rootkits

The third article in the series will focus on rootkit talk given by Bill Demirkapi (@BillDemirkapi). In his talk, Bill explained how rootkits are created and loaded, how the attacker can establish a stealth communication channel with the rootkit, and how to cover up rootkit traces.

Rootkits

Let’s start with a question: what is a rootkit?

A rootkit is a malicious piece of code that, among its various aims, tries to stay undetected. It is designed to enable access to a computer or an area of its software that is not otherwise allowed (for example, to an unauthorized user) and often masks its existence or the existence of other software. There are rootkits for various operating systems, but the presentation was focused on the Windows operating system. We can distinguish two types: user-mode rootkits and kernel-mode rootkits. The latter is much more powerful, as it can run on the same level as all kernel code. There is not much mitigation available for kernel-code, although there are some, such as Kernel Patch Protection (KPP, A.K.A. PatchGuard) which will simply crash the system if any malicious code is detected. Besides that, kernel drivers can do almost whatever they want – they have full access to the machine, and they run at the same level as Anti-Virus software (hence, they are mostly trusted or ignored by AVs). Common memory space is shared between drivers and it’s trivial to crash the entire OS with a single write to the invalid memory region.

 

Code signing

But hey – aren’t the drivers supposed to be trusted? Windows 10 (starting from version 1607) requires every driver to be properly signed with a trusted Extended Validation Code Signing Certificate. Attackers cannot just create a fresh cert and sign the code. Windows requires a specific root of trust (hardcoded in the kernel itself) to be used. The attacker could register the company, acquire EV-cert, and initiate the certification process, but hiding the malicious intentions might be tricky, expensive, and at the same time this would expose the true identity of the rootkit author.

There are, however, many known vulnerable drivers – especially OEM drivers – properly signed and usable. They have well-documented issues. Some of them will allow you to just write arbitrary memory locations, some of them will expose insecure IOCTLs, and some will allow you to run arbitrary code in kernel-mode. Game hacking forums are full of such examples – they are often used by game hackers to create game cheats. Some of the anti-cheat software runs in user-mode, so having the ability to run kernel-mode code is priceless in terms of anti-cheat bypasses. Bill mentioned that this isn’t a preferred method due to the high risk of triggering BSOD or other compatibility issues. Rootkit needs to have no visible impact on system stability.

The other option is to craft a malicious driver and sign it using *someone else’s* certificate. The certificates are sometimes stolen or leaked and can be found in the depths of the Internet. If an older version of Windows is targeted, non-EV certs will also work. On the latest Windows, non-EV will work only if the secure boot is disabled.

Redacted examples of leaked code-signing certificates.

Redacted examples of leaked code-signing certificates.

Side note: mimidrv, that comes with Mimikatz, was developed by Benjamin Delpy and is a legitimately signed kernel driver. It uses an old, but still accepted certificate. There is an exception for drivers signed before 2015-07-29, hence new mimidrv releases can still be signed and loaded by modern Windows systems. There seems to be a deadline, though. 

 

Communication

Once rootkit driver is loaded – how to communicate with it? Sure, there are standard methods, like exposed IOCTLs, but such comm will be easily detected and analyzed. This is also something available locally, not from an external network. Bill came up with another idea to “blend in with the noise” of network traffic. Opening a port is just too obvious, piggybacking on other protocols – much harder to spot, but not so flexible if a single app protocol needs to be followed. Instead, he decided to hook on network communication – sort of like sniffing software – and look for packets with certain magic sequence (e.g. 0xDEADBEEF). To work with network packets, rootkit needs to intercept IOCTLs on WinSock driver, which allows us to inspect all network data. To intercept that, the rootkit has to either replace Major Function (used to handle all packets) or hook one of the handlers directly. Both methods have some drawbacks and can also be quite easily detected by AVs looking for modifications on driver’s dispatch routines. Directly hooking a handler will also not work unless HVCI (Hypervisor-Protected Code Integrity) is bypassed.

If both methods are far from perfect… Then the undocumented method needs to be used! Bill decided to not hook the driver object, but the file object instead. The object file, defined by the FILE_OBJECT structure, has a pointer to the device object which handles the IRPs (Interrupt Request Packets). By replacing the file object and creating a new instance of device object, the rootkit will get all network traffic. The new driver object’s dispatch hook needs to call its custom function (to scan the packets) and original dispatch function (so that everything keeps working as expected). On cleanup, it’s important to restore the original structure to prevent any weird issues. The presentation contains full details on which exported functions to use to create new objects. More info is published on the project’s wiki.

Having full control over traffic, the rootkit can peek into packets, look for those with special magic sequences, and retrieve additional data, that otherwise is ignored. That data may contain commands to execute or hidden ingress traffic. An example type of supported packets is the special PingPacket. The infected system may respond to it, while the normal system will simply treat it as a normal ICMP ping.

Did someone say commands to execute? Running typical user-mode code from the kernel-mode driver is not straight-forward. Additional user-mode driver or APC communication is typically used to achieve that, but Bill decided to use Windows (un)named pipes instead. The created cmd.exe will have a hidden window and will redirect the command output to the pipe object. Of course, all that isn’t straight-forward either, as additional structures need to be created manually.

kernel-mode <--> user-mode communication

kernel-mode <–> user-mode communication (source: BHUSA presentation)

 

Hiding file object

Mini-filters were used to redirect access from the legitimate (and unsuspected) file to a malicious driver. Will that trigger any AV? It depends, mini-filters are quite popular and can be easily enumerated (check on your system with fltmc filters command run as an administrator). Another method would be to use the existing Mini-Filter. Instead of hooking it directly, Bill decided to use callbacks. A callback function can be registered for each mini-filter instance. Once everything is set up, each access request will be checked, and if a specific path is used, the STATUS_REPARSE will be returned to use another file instead. Reparsing is just a standard file system event so unless someone is analyzing it with procmon or a similar tool, it may stay undetected for a while.

 

Summary

The presented technique of course assumes initial administrator access (to load even signed driver, SeLoadDriverPrivilege is required). Once the rootkit driver is loaded, it can operate in a stealth manner, while giving remote code execution capabilities to anyone capable of sending network packets to the system. Unless air-gapped, even a firewalled system receives DNS replies which could contain additional magic sequence and crafted commands. The proposed techniques are also valid in terms of code integrity and KPP protections and may stay under AV radar for a reasonable amount of time. The full source code of Spectre rootkit was released on the author’s github. I encourage everyone to give it a look as it contains several additional tricks (e.g. support for multiple encapsulation layers for outgoing packets).

 

This image has an empty alt attribute; its file name is untitled-design-2.png

Adrian Denkiewicz, Cybersecurity Specialist at CQURE, Ethical Hacker, Penetration Tester, Red Teamer, Software Developer, and Trainer. Holder of OSCE, OSCP, OSWP, and CRTE certificates. Adrian is deeply interested in the offensive side of security, ranging from modern web attacks, through operating system internals, to low level exploit development.  Twitter: @a_denkiewicz

Comments