A little over four years ago, ‘DLL hijacking’ was all the rage and countless applications were found to be vulnerable. Today, due to protection mechanisms such as ‘SafeDllSearchMode’ and wiser (security-aware) developers, this class of attacks is often assumed to be extinct. Sadly as this blog post will show, this is not necessarily the case. Specifically, we will show how a recently released security tool (‘Deteckt’), is susceptible to ‘DLL hijacking’ and how this flaw may be abused to elevate privileges as well as perform other nefarious actions.
Detekt is Released
Recently a security tool named Detekt was released, which claimed to scan computers for “traces of known surveillance spyware used to target and monitor human rights defenders and journalists around the world.” Leveraging yara-based signatures, Detekt uses open-sourced memory forensics components to scan for malware such as FinFisher FinSpy and Hacking Team RCS. While the media swooned, critics were quick to point out shortcomings. However, when Detekt found a “new undetected Hacking Team sample” its merit was clearly confirmed. This is an important point to understand, as the goal of this blog is not to criticize Detekt, but rather to use it as a case study to illustrate that ‘DLL hijacking’ is alive and well – even in 2014!
figure 0x1: Detekt success!
The day Detekt was released I happened to be stuck on an airplane flying cross-county, so I decided to poke on Detekt. Besides uncovering a number of trivial bypasses (e.g. simply name your malware to match a process in the whitelist), I noticed that Detekt would first attempt to load several system DLL’s from its current directory before checking for their presence in the OS’s system directory. In other words, it was susceptible to a classic ‘DLL hijack’ attack. (Note: All tests performed on a fully-patched Windows 7 64-bit box with ‘SafeDllSearchMode’ enabled).
Hijacking Detekt (Implications)
Before diving into some technical details, it’s important to briefly touch on some of the implications of this classic vulnerability.
First, Detekt will only execute properly when run in a privileged manner (i.e. ‘Run as administrator’ with a UAC prompt). As such, if it is run from an unprivileged directory (e.g. the Desktop/ or Downloads/ folder), unprivileged malware could abuse the ‘DLL hijack’ to escalate its privileges to SYSTEM.
Second, since the ‘DLL hijack’ will allow for arbitrary code to be loaded into the process context of Detekt, it would be trivial for malware to abuse this and subvert Detekt in order to thwart detection.
Finally, as Detekt installs a kernel-mode driver to scan memory, malware (within Detekt’s context), could equally leverage this driver to read arbitrary system memory…ouch!
This issue was immediately reported. Interestingly, although multiple versions of Detekt have been subsequently released there has been no response, and the latest version is still vulnerable.
Hijacking Detekt (Details)
‘DLL hijacking’ is a well understood class of attacks. In short, if an application (or the Window’s loader) first looks for a required DLL in a directory where the DLL does not exist, an attacker can place a malicious DLL in that location and it will be automatically loaded. (For more information, see “What is DLL Hijacking?”).
The following steps illustrate how an attacker would likely find and successfully exploit a ‘DLL hijack.’
- 1. Find a DLL to hijack
- 2. Ensure existing functionality is not broken
- 3. Perform some previously unpermitted/malicious actions
Step 1: Find a DLL to hijack
As shown in figure 0x2, when the Detekt process is launched, it looks for various system DLLs in its current working directory (e.g. Downloads/). Unsurprisingly, these system DLLs are not found there, as they are installed in %WINDIR%system32 or %WINDIR%SysWoW64. As such, if we place an arbitrary DLL in Detekt’s currently working directory (with a name that matches one of the ‘NAME NOT FOUND’ DLLs), whenever Detekt is run it will find this malicious DLL and load it instead.
This is trivial to test and confirm. First create a DLL that performs some observable action (e.g. logs to a file) in its DllMain function (case DLL_PROCESS_ATTACH). Then simply copy this DLL to Detekt’s directory, naming the DLL ‘dnsapi.dll’
Now, whenever Detekt is executed the malicious ‘dnsapi.dll’ will be automatically loaded into Detekt’s process space. This may seem like no big deal, however since Detekt must be run in a privileged manner, the malicious DLL will be elevated to a privileged context as well. Since creating and copying a DLL to Detekt’s directory are not privileged operations, an elevation of privileges (EoP) has just occurred!
Step 2: Ensure existing functionality is not broken
Without taking proactive steps, blindly hijacking a system DLL (e.g. ‘dnsapi.dll’) will likely break the functionality of Detekt. Why? Well, when Detekt or one of its DLLs loads ‘dnsapi.dll,’ it expects it to be able to perform some required DNS functionality. Since the legitimate ‘dnsapi.dll’ was hijacked and instead a malicious DLL was loaded in place (unbeknownst to Detekt), the DNS-related exports will not be available. Depending on how these exports are resolved and/or used, this can lead to a variety of noticeable issues such as program crashes or freezes. To avoid these issues, the malicious DLL must somehow re-implement or restore the original exports of the system DLL.
While there are several ways to ensure that normal functionality is restored, the simplest and most reliable way is to utilize export forwarders. Simply put, by using export forwarding, (as its name implies), all requests for exported functions will be seamlessly and transparently redirected (i.e. forwarded) elsewhere. Thus, the malicious DLL can use export forwarding to proxy all requests to the legitimate (original) ‘dnsapi.dll.’ This will restore functionality and ensure that Detekt is able to function as expected. (It should be noted that this techniques has been used before in malware such as Stuxnet, which hijacked s7otbxdx.dll and forwarded the majority of exports to the real DLL).
In order to forward exports, one has to first identify the exports to forward. This can be accomplished with a tool such as dumpbin with the /exports commandline option:
figure 0x5: exports for the real ‘dnsapi.dll’
For each export, an export forwarding directive should be placed in the source code of the malicious DLL. When compiled, the malicious DLL will then contain the necessary information in its export address table in order to ensure that requests for exports will be transparently forwarded to the original DLL.
The easiest way to create an export forwarding directive is via a #pragma comment for the linker:
#pragma comment(linker, "/export:<function to export>=<name of DLL with export>.<exported function>")
For example, to export forward the DnsResolverOp function, our export forwarding directive would look as follows:
#pragma comment(linker, "/export:DnsResolverOp=C:\Windows\SysWOW64\dnsapi.DnsResolverOp")
When compiled, any request for the DnsResolverOp function will now be transparently forwarded to the real ‘dnsapp.dll’ where the DnsResolverOp function resides. As such, no functionality is broken and Detekt will work as expected.
It should be noted, that an export forwarding directive must be created for each and every export! Since the real ‘dnsapi.dll’ exports over 200 functions, manually creating each directive would be a tedious and time-consuming task. Luckily a python script can automate this task and create a header file that contains all the required export forwarding directives – sweet!
Including the created header file ‘dnsapi_fwd.h’, produces all the necessary export forwarding directives in the compiled binary (note the “forward to…”):
figure 0x7: malicious DLL’s forwarded exports
Step 3: Perform some previously unpermitted/malicious action
At this point, we’ve shown how an attacker can automatically coerce Detekt to load an arbitrary DLL into its elevated process context. With such elevated privileges, an attacker can easily elevate to SYSTEM and completely own the system
But let’s have some fun. Since we are running within the process space of Detekt we can easily subvert, or worse yet, leverage Detekt for nefarious purposes. First, we can easily subvert Detekt to show that nothing is detected, even if something malicious like FinFisher is running.
figure 0x8: subverting Detekt (before/after)
Second, we can also easily leverage Detekt in order to perform other malicious actions – for example, dump kernel memory. In order to scan system memory for malware, Detekt installs and loads a kernel driver, winpmem.sys, which is capable of reading arbitrary memory. Though access to this driver (via \.pmem) is restricted, the malicious DLL is loaded into the address space of Detekt so it can leverage the driver as well. In other words – unrestricted read-access of system memory!
figure 0x9: ‘sharing’ Detekt’s kernel driver to read raw memory
Today we’ve shown that ‘DLL hijacking’ attacks are alive and well. We used Detekt as a case study, not to discredit the tool (I’d still highly recommend running it), but rather as it was a relevant candidate for a case study. Via a ‘DLL hijack’ it provided a trivial EoP and exposed several capabilities that were easily abused. Of course, it should be noted that (though unlikely), malware would have to have a priori knowledge of this attack vector in order to target Detekt. Therefore, if I was a FinFisher author or employed by the Hacking Team, I’d push out a quick update!
figure 0x0A: attack automation