DLL Hijacking for Code Execution

Posted on Feb 18, 2023

Introduction

Although not a novel concept, DLL Hijacking or proxying has proven to be a valuable technique in various scenarios, such as software exploitation, as well as being employed in the cracking of common software and game hacking.

To begin with, you may be unfamiliar with or curious about the concept of DLLs. DLL, short for Dynamic Link Library, refers to the essential libraries that contain functional code or resources utilized in Windows applications. These libraries share the same Portable Executable (PE) file format as .EXE files. In this post, I will provide a brief overview of their structure to facilitate an understanding of the DLL proxying process.

Microsoft’s implementation of the shared library concept enables code to be shared across multiple applications. For instance, an application named ‘ApplicationA’ can load a library called Common.DLL and invoke a function contained in that library which performs the addition of two integers. Similarly, ‘ApplicationB’ can also load the same library and invoke the identical function, which is essentially shared between the two applications. To facilitate this, libraries expose functions which are stored in a structure referred to as the export table within the DLL. For further information on the PE format, please refer to the following resource.

Windows DLL Loader (LdrLoadDLL)

The Windows DLL Loader, also known as the PE Loader, is responsible for loading these libraries during the startup of a program or at runtime when they are needed. When attempting to load a library, the loader follows a specified search order. The following list displays the default order in which the loader searches for the requested library to be loaded. Please note that the order may differ if safe DLL search mode is disabled.

  1. Initially, the loader attempts to locate the library within the process memory to determine if it has been previously loaded.

  2. Next, it searches for the library in the KnownDLLs registry entry located at: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs, to identify any matches by name.

  3. The subsequent location is the application directory. This directory is of importance to us for this method to work.

  4. If the library is not found in the preceding steps, the loader looks in the System32 directory at C:\Windows\System32 followed by the 16 bit directory located at C:\Windows\System.

  5. The loader then attempts to locate the library in the Windows folder at C:\Windows.

  6. It also checks the Current Directory, which refers to the directory from which the application is running.

  7. Lastly the directories that are listed in the PATH environment variable are searched.

For a more comprehensive explanation of each location, please refer to the Microsoft documentation.

How does DLL proxying or hijacking work?

DLL proxying or hijacking exploits the DLL search order to load a malicious DLL library into the target application. If an application attempts to load a DLL without specifying its path, the Library Loader will search for it using an ordered list. Once it locates a file with the same library name, it will load it into the application’s process memory and prepare it to execute one of its exported functions.

To hijack this process, a crafted DLL can be placed in a location that appears early on in the search order, allowing it to be loaded for execution instead of the original intended library, which may be located further down the search list.

However, to ensure stable execution, a DLL wrapper must be created to expose the same functions exported by the original DLL library. Failure to do so will result in a failed function call when the malicious library is loaded.

Here’s a diagram I’ve illustrated to explain this concept:

Locating susceptible DLLs

We can utilize a tool named Process Monitor by Mark Russinovich to locate libraries that are susceptible to such an attack. It has the ability to monitor library load calls and using a certain filter it is possible to discover DLLs that fall into that category.

In the example below I will explain how code execution can be acquired on Microsoft Teams by using DLL hijacking. This method is utilized by my AnonPresence tool which blocks telemetry and presence data from being sent back to Microsoft.

Process Monitor

The filter option inside of ProcMon is vital to helping us find what we are looking for.

By applying a filter to the Result field to only search for entries that return NAME NOT FOUND, we can observe that many CreateFile operations return this result once the application is launched.

We now have a list of possible libraries to target.

Creating a Wrapper/Proxy DLL Library

Continuing with the previous example involving Microsoft Teams, we can select one of the DLLs listed to perform the DLL hijacking. In this demonstration, I have opted to use the VERSION.DLL library, which provides helper functionality for managing files and file attributes.

To accomplish this, we need to create a C++ Native DLL project that exposes the same functions exported by the VERSION.DLL library.

Function Exports

By opening the original VERSION.DLL library in PeStudio, we can examine the functions it exports.

Usually you can locate the original file (if it exists) in the System32 directory. If not you can do a quick google search and find a download for your Windows version.

To proceed, we need to replicate the functions exported by the original DLL in our wrapper DLL library. However, rather than copying the implementation, we will redirect these functions back to the original DLL.

To do this we can use the comment pragma like so:

#pragma comment(linker,"/export:NameOfExportFunction=TargetModule.NameOfExportFunction,@1")

TargetModule refers to the module that contains the actual implementation, while NameOfExportFunction refers to the name of the function that you want to expose and forward.

An example of what I’m talking about can be found here for version.dll.

Deployment

The final step is renaming the original DLL Library to version_orig.dll and our newly compiled wrapper with the original’s name version.dll.

Both files then can be dropped inside the application folder next to the Teams.exe executable. This directory is typically located at:

C:\Users\%USERPROFILE%\AppData\Local\Microsoft\Teams\current

Execution

Now when the Teams executable is run the DLLEntryPoint of our crafted DLL will be executed immediately. We have successfully gained code execution.

Conclusion

In this post, we have explored how the DLL load order in Windows can be exploited to achieve code execution via DLL hijacking. By understanding the way DLL libraries are loaded in processes, we can prevent such exploits by taking appropriate measures.

One effective method is to sign libraries with a digital signature and verify that the files being loaded are legitimate by checking the digital signature on the file. By taking these steps, we can avoid loading invalid or unsigned files that may contain malicious code.

To ensure safe and secure code execution, it is essential to prioritize security when using DLL libraries and take the necessary precautions. However, it is worth noting that even signed modules may not provide foolproof protection since there are methods to attack the Windows functions responsible for verifying the integrity of signed binaries. Perhaps in a future post, we can delve into this topic further. 😉