Windows DLL Search Order
When a Windows program loads a DLL by name (rather than full path), the loader walks a fixed sequence of directories and takes the first match. Whoever controls an early directory in that sequence controls what code the program runs — this is DLL search-order hijacking, MITRE T1574.001, used for persistence, privilege escalation (plant the DLL where a higher-privileged program will load it), and execution-policy evasion (a DLL isn’t a “script” AppLocker rules usually gate).
The two standard orders
The sequence depends on SafeDllSearchMode (registry HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode, enabled by default since XP SP2):
Enabled (default):
- Application directory (where the EXE lives)
- System directory (
GetSystemDirectory()→C:\Windows\System32) - 16-bit system directory (
C:\Windows\System) - Windows directory (
GetWindowsDirectory()→C:\Windows) - Current directory
- Directories in
PATH
Disabled:
- Application directory
- Current directory ← moved up to position 2
- System directory
- 16-bit system directory
- Windows directory
PATH
The whole point of safe mode is demoting the current directory — the classic “binary planting” vector where a victim opens a document from an attacker-controlled share and the app loads a malicious DLL from that same share. Disabling it (some legacy apps required it) is itself a finding.
Packaged apps and LOAD_WITH_ALTERED_SEARCH_PATH / SetDllDirectory change the order further; the two lists above are the desktop default that matters for most hunting.
Attack mechanics
- Phantom DLLs: target a DLL the program requests that doesn’t exist anywhere — plant your own with the right name anywhere in the order. No need to beat the real file, just to exist.
- Pre-planting: place a same-named DLL in a directory searched before the legitimate one (a writable app directory, the CWD, an early PATH entry). Malicious DLLs often proxy exports to the real library so the victim program behaves normally.
- Privilege crossover: if the search-order-vulnerable program runs elevated (a service, a scheduled task, an auto-elevated signed binary), your DLL inherits the privilege.
Finding hijackable targets
The reconnaissance problem is observability: seeing which names a program probes and fails to find requires watching file-open syscalls, which in practice means running Process Monitor (ProcMon) with a Result = NAME NOT FOUND, Path ends with .dll filter — and that generally needs admin rights already. So DLL-hijack discovery is typically a post-privesc or lab activity, while the exploitation only needs write access to a directory that precedes the legitimate one in the order (audit those with icacls).
Writing a malicious DLL
A stub DLL needs only a DllMain that fires on DLL_PROCESS_ATTACH. This one writes proof-of-execution to a file, then exits the host process cleanly so the victim doesn’t linger:
#include <windows.h>
BOOL WINAPI DllMain
(HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
if (dwReason == DLL_PROCESS_ATTACH) {
system("cmd.exe /C whoami > C:\\Temp\\dll.txt");
ExitProcess(0);
}
return TRUE;
}Cross-compile it to a Windows DLL from Linux with mingw:
x86_64-w64-mingw32-gcc windows_dll.c -shared -o output.dllDrop output.dll at a search-order position ahead of the legitimate library (a writable app directory, the CWD, an early PATH entry) with the name the target program probes for. In a real engagement the payload proxies the genuine exports so the victim program still functions; the stub above just proves the load happened.
Mitigations
Per Microsoft’s DLL security guidance and MITRE M1044/M1038:
- Keep SafeDllSearchMode enabled (default; verify the registry value on legacy estates)
- Disallow remote DLL loading (CWDIllegalInDllSearch) — default in Server 2012+, patch-available elsewhere; kills WebDAV/share-based planting
- Application control (WDAC/AppLocker DLL rules) to block unsigned DLLs loading into signed processes
- Developers: call
LoadLibraryExwithLOAD_LIBRARY_SEARCH_*flags or full paths; never rely on CWD
Sources
- Microsoft Learn — Dynamic-link library search order
- Microsoft Learn — Dynamic-Link Library Security
- MITRE ATT&CK — T1574.001 Hijack Execution Flow: DLL Search Order Hijacking
Related
- icacls — auditing which search-path directories are writable by low-privileged users
- windows-services — services are the highest-value hijack targets (auto-start, often SYSTEM)
- windows-file-association-hijacking — sibling “change what the OS resolves a name to” persistence class (T1546.001)