时时勤拂拭,勿使惹尘埃

TOC

Categories

DLL劫持漏洞及检测


0x0 dll加载方式 

静态链接库在链接时,编译器会将 .obj 文件和 .LIB 文件组织成一个 .exe 文件,程序运行时,将全部数据加载到内存。

如果程序体积较大,功能较为复杂,那么加载到内存中的时间就会比较长,最直接的一个例子就是双击打开一个软件,要很久才能看到界面。这是静态链接库的一个弊端。

动态链接库有两种加载方式:隐式加载和显示加载。

  • 隐式加载又叫载入时加载,指在主程序载入内存时搜索DLL,并将DLL载入内存(程序无法进行校验)。隐式加载也会有静态链接库的问题,如果程序稍大,加载时间就会过长,用户不能接受。
  • 显式加载又叫运行时加载,指主程序在运行过程中需要DLL中的函数时再加载(程序可以进行校验)。显式加载是将较大的程序分开加载的,程序运行时只需要将主程序载入内存,软件打开速度快,用户体验好。

隐式加载和显式加载这两种加载DLL的方式各有优点,如果采用动态加载方式,那么可以在需要时才加载DLL,而隐式链接方式实现起来比较简单,在编写程序代码时就可以把链接工作做好,在程序中可以随时调用DLL导出的函数。

0x01 隐式加载

要通过隐式链接使用 DLL,客户端可执行文件必须从 DLL 的提供者处获取这些文件:

  1. 一个或多个头文件(.h 文件),其中包含 DLL 中导出的数据、函数和 C++ 类的声明。DLL 导出的类、函数和数据都必须__declspec(dllimport)在头文件中进行标记。有关详细信息,请参阅dllexport、dllimport
  2. 一个导入库,用于链接到可执行文件。链接器在构建 DLL 时创建导入库。有关详细信息,请参阅LIB文件作为链接器输入
  3. 实际的 DLL 文件。

要通过隐式链接使用 DLL 中的数据、函数和类,任何客户端源文件都必须包含声明它们的头文件。从编码的角度来看,对导出函数的调用就像任何其他函数调用一样。

要构建客户端可执行文件,必须链接到 DLL 的导入库。如果使用外部 makefile 或构建系统,请将导入库与链接的其他目标文件或库一起指定。

操作系统在加载调用可执行文件时必须能够定位 DLL 文件。这意味着必须在安装应用程序时部署或验证 DLL 的存在。

0x02 显示加载

要通过显式链接使用 DLL,应用程序必须进行函数调用以在运行时显式加载 DLL。要显式链接到 DLL,应用程序必须:

  1. 调用LoadLibraryEx或类似函数来加载 DLL 并获取模块句柄。
  2. 调用GetProcAddress以获取指向应用程序调用的每个导出函数的函数指针。由于应用程序通过指针调用 DLL 函数,编译器不会生成外部引用,因此无需与导入库链接。但是,您必须有一个typedeforusing语句来定义您调用的导出函数的调用签名。
  3. 完成 DLL 后调用FreeLibrary

LoadLibrary系列函数有如下几个:

  1. LoadLibraryA
  2. LoadLibraryExW
  3. LoadLibraryExW
  4. LoadLibraryW
  5. AfxLoadLibrary/AfxLoadLibraryEx

0x1 DLL劫持

0x11 DLL劫持漏洞原理

当程序加载一个 DLL 文件,且没有指定绝对路径时,操作系统会协助程序查找所需的 DLL 文件,这个查找顺序是固定的。

因此,如果程序加载某个 DLL 文件时,而一个同名的 DLL 文件刚好就在优先查找目录中,如程序当前目录,那么操作系统会优先将该 DLL 文件进行加载。

如果应用程序以管理员权限运行,攻击者可能会成功提升本地权限。

另外,如果程序在加载DLL时,未对DLL文件进行签名/hash校验,那么攻击者也可以替换掉原始DLL文件,进行DLL劫持攻击。

0x12 DLL搜索顺序

系统使用的标准 DLL 搜索顺序取决于是否启用了安全 DLL 搜索模式。安全 DLL 搜索模式将用户的当前目录放在搜索顺序的后面。

默认情况下启用安全 DLL 搜索模式。要禁用此功能,请创建HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode注册表值并将其设置为 0。调用SetDllDirectory函数会在指定目录位于搜索路径中时有效禁用SafeDllSearchMode ,并按所述更改搜索顺序在这个话题中。

如果启用SafeDllSearchMode,则搜索顺序如下:

 1. 加载应用程序的目录。
 2. 系统目录。使用`GetSystemDirectory`函数获取该目录的路径。
 3. 16 位系统目录。
 4. Windows 目录。使用`GetWindowsDirectory`函数获取该目录的路径。
 5. 当前目录。
 6. PATH 环境变量中列出的目录。请注意,这不包括`App Paths`注册表项指定的每个应用程序路径。计算DLL 搜索路径时不使用`App Paths`键。

如果禁用SafeDllSearchMode,则搜索顺序如下:

 1. 加载应用程序的目录。
 2. 当前目录。
 3. 系统目录。使用`GetSystemDirectory`函数获取该目录的路径。
 4. 16 位系统目录。
 5. Windows 目录。使用`GetWindowsDirectory`函数获取该目录的路径。
 6. PATH 环境变量中列出的目录。请注意,这不包括`App Paths`注册表项指定的每个应用程序路径。计算DLL 搜索路径时不使用`App Paths`键。

更多不同环境下搜索顺序参考:Dynamic-Link Library Search Order

0x13 微软DLL安全加载指南

Dynamic-Link Library Security

开发人员可以通过遵循以下准则来帮助保护他们的应用程序免受 DLL 预加载攻击:

 1. 在使用`LoadLibrary`、`LoadLibraryEx`、`CreateProcess`或`ShellExecute`函数时,请尽可能指定完全限定的路径。
 2. 将 `LOAD_LIBRARY_SEARCH`标志与`LoadLibraryEx`函数一起使用,或将这些标志与`SetDefaultDllDirectories`函数一起使用来为进程建立 DLL 搜索顺序,然后使用`AddDllDirectory`或`SetDllDirectory`函数来修改列表。有关详细信息,请参阅动态链接库搜索顺序。
 3. Windows 7、Windows Server 2008 R2、Windows Vista 和 Windows Server 2008:这些标志和功能在安装了KB2533623的系统上可用。
在安装了 KB2533623 的系统上,将` LOAD_LIBRARY_SEARCH `标志与`LoadLibraryEx`函数一起使用,或将这些标志与`SetDefaultDllDirectories`函数一起使用来为进程建立 DLL 搜索顺序,然后使用`AddDllDirectory`或`SetDllDirectory`函数来修改列表。有关详细信息,请参阅动态链接库搜索顺序。
 4. 考虑使用DLL 重定向或清单来确保您的应用程序使用正确的 DLL。
 5. 使用标准搜索顺序时,请确保已启用安全 DLL 搜索模式。这会将用户的当前目录放在搜索顺序的后面,从而增加了 Windows 在恶意副本之前找到 DLL 的合法副本的机会。从带有 Service Pack 2 (SP2) 的 Windows XP 开始,默认启用安全 DLL 搜索模式,并由`HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode`注册表值控制。有关详细信息,请参阅动态链接库搜索顺序。
 6. 考虑通过使用空字符串 ("")调用`SetDllDirectory`从标准搜索路径中删除当前目录。这应该在进程初始化的早期完成一次,而不是在调用`LoadLibrary`之前和之后。请注意,`SetDllDirectory`会影响整个进程,并且使用不同值调用`SetDllDirectory`的多个线程可能会导致未定义的行为。如果您的应用程序加载第三方 DLL,请仔细测试以识别任何不兼容性。
 7. 除非启用了安全进程搜索模式,否则不要使用`SearchPath`函数为后续的`LoadLibrary`调用检索 DLL 的路径。当未启用安全进程搜索模式时,`SearchPath`函数使用与`LoadLibrary`不同的搜索顺序,并且可能首先在用户的当前目录中搜索指定的 DLL。要为`SearchPath`函数启用安全进程搜索模式,请将`SetSearchPathMode`函数与 `BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE` 一起使用。这会将当前目录移动到`SearchPath`的末尾进程生命周期的搜索列表。请注意,当前目录并未从搜索路径中删除,因此如果系统在到达当前目录之前没有找到 DLL 的合法副本,则应用程序仍然存在漏洞。与`SetDllDirectory`一样,调用`SetSearchPathMode`应该在进程初始化的早期完成,它会影响整个进程。如果您的应用程序加载第三方 DLL,请仔细测试以识别任何不兼容性。
 8. 不要根据搜索 DLL的`LoadLibrary`调用来假设操作系统版本。如果应用程序在 DLL 合法不存在但 DLL 的恶意副本位于搜索路径中的环境中运行,则可能会加载 DLL 的恶意副本。相反,请使用获取系统版本中描述的推荐技术。

0x2 检测方式

  • 静态:扫描DLL加载代码,检测加载的dll路径是否使用相对路径
  • 动态:枚举进程调用的dll列表,解析出dll的名称,将测试dll分别重命名为列表中的dll名称

0x21 动态检测(行业通用)

可以在目标程序所在目录中,放置其所需要加载的 DLL文件的同名文件,来判断该 DLL 文件是否可以被加载并执行,如果可以,则证明存在 DLL 劫持漏洞。

因此,检测方式如下:

 1. 将目标程序运行起来。
 2. 获取目标进程当前加载的 DLL 列表。
 3. 关闭目标进程。
 4. 根据 DLL 列表,将提前准备好的测试 DLL 文件改名后放置在目标程序所在目录。
 5. 运行目标程序,检测测试 DLL 文件是否被成功加载。
 6. 如果测试 DLL 被加载,则证明存在漏洞,否则,则没有。
 7. 关闭目标进程。
 8. 重复第 4 、5、6、7 步,直到 DLL 列表中所有条目均已测试完毕。

已有完整DLL劫持漏洞检测工具,如:

Rattler

存在问题:
显示加载原理可知,dll只有在使用到时才会加载,部分功能如果隐藏较深,运行未使用到该功能,很容易漏报

0x22 Process Monitor监控(微软提供方式)

Process Monitor 工具可用于帮助识别可能易受攻击的 DLL 加载操作。

以下过程描述了如何使用进程监视器来检查应用程序中的 DLL 加载操作:

  1. 启动进程监视器。
  2. 在 Process Monitor 中,包括以下过滤器:
    • Operation is LoadImage
    • Operation is CreateFile
    • Path contains .cpl
    • Path contains .dll
    • Path contains .drv
    • Path contains .exe
    • Path contains .ocx
    • Path contains .scr
    • Path contains .sys
  3. 排除以下过滤器::
    • Process Name is procmon.exe
    • Process Name is Procmon64.exe
    • Process Name is System
    • Operation begins with IRPMJ
    • Operation begins with FASTIO_
    • Result is SUCCESS
    • Path ends with pagefile.sys
  4. 尝试在当前目录设置为特定目录的情况下启动应用程序。
  5. 检查进程监视器输出中的可疑路径,例如调用当前目录以加载 DLL。这种调用可能表明应用程序存在漏洞。

    存在问题:
    显示加载原理可知,dll只有在使用到时才会加载,部分功能如果隐藏较深,运行未使用到该功能,很容易漏报

0x23 源码检测

  • 隐式加载:检索__declspec(*)函数
  • 显示加载:检索*LoadLibrary*CreateProcessShellExecute等函数,判断dll是否绝对路径,并是否验证签名/hash(若不验证签名/hash,dll被篡改后依然可以进行劫持,常见利用dll劫持提权情况)

    存在问题:
    如果对LoadLibrary相关函数进行检测路径/签名/hash验证等,因为建模、检测精度等问题(源代码检测行业普遍存在),会有漏报甚至是一定的误报的可能性;如果使用人工分析判断,会消耗大量时间精力

0 评论:

发表评论