DLL Hell问题与MS的解决方案导致的Bypass UAC

  1. DLL Hell问题
  2. DLL Redirection(DLL重定向)
  3. Side-by-Side Components(WinSxS)
  4. Bypass UAC问题的产生
  5. 参考

author: Dlive

DLL Hell问题

DLL Hell问题的场景如下:

应用程序 a.exe 依赖动态链接库 compoent.dll 1.0 版本。

但是用户的另一个软件 b 在系统的系统目录安装了 component.dll 2.0 版本,这两个版本完全不兼容,

因此,Windows 在加载 component.dll 的时候,会直接加载系统目录中的 component.dll,

这就造成了 a.exe 程序无法运行,如果这时用户需要同时使用两个软件,就会造成所谓的 DLL Hell。

微软为解决这个问题,提出了两种解决方案:DLL重定向和SxS(Side by Side组件)

MSDN(https://msdn.microsoft.com/en-us/library/ms682600(VS.85).aspx)中给出的两种解决方案的应用场景:

Developers and administrators should use DLL redirection for existing applications, because it does not require any changes to the application. If you are creating a new application or updating an application and want to isolate your application from potential problems, create a side-by-side component.

DLL Redirection(DLL重定向)

该技术适用于Windows 2000及以后的操作系统

这里有两种方法可以实现动态链接库重定向技术。

1.创建 a.exe.local 文件,该文件内容为空。

这时系统就会强制使 a.exe LoadLibrary 时先在 a.exe 所在的目录下查找要加载的动态库,之后才到系统目录下寻找。这个解决方法适用于两个不同的应用。

2.创建 a.exe.local 目录,将 a.exe 依赖的库放入其中。

这时系统就会强制使 a.exe LoadLibrary 时先在 a.exe.local 目录下查找要加载的动态库,之后才到系统目录下寻找。这个解决方法适用于单个应用中存在两个应用程序,并且以来同名但是互不兼容的库。

动态链接库重定向技术实际上是 MS 修改了 LoadLibrary(Ex) 的代码,使其在调用时先检测是否存在 .local 文件或目录实现重定向的。它是作为临时解决 DLL HELL 的方法。并且,当应用程序存在 Manifest 时,该技术将不会起作用。

MSDN原文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

if the application c:\myapp\myapp.exe calls LoadLibrary using the following path:

c:\program files\common files\system\mydll.dll(EXE中使用硬编码绝对路径加载DLL,系统也会先去找.local)

And, if both c:\myapp\myapp.exe.local and c:\myapp\mydll.dll exist,

LoadLibrary loads c:\myapp\mydll.dll.

Otherwise, LoadLibrary loads c:\program files\common files\system\mydll.dll.

Alternatively, if a directory named c:\myapp\myapp.exe.local exists and contains mydll.dll,

LoadLibrary loads c:\myapp\myapp.exe.local\mydll.dll.

系统Dll不会受DLL重定向机制的影响

Known DLLs cannot be redirected. For a list of known DLLs, see the following registry key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs.

举个栗子🌰

DotLocal.exe代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

#include "stdafx.h"

#include <windows.h>

#include <stdio.h>



int _tmain(int argc, _TCHAR* argv[])

{



HMODULE dll = LoadLibrary(TEXT("D:\\test\\poc.dll"));//该路径不存在

if (dll == NULL){

printf("dll no load");

}

system("pause");

return 0;

}

poc.dll代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

BOOL APIENTRY DllMain( HMODULE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

)

{

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

MessageBoxA(NULL,"Hello!", "Hello", MB_OK);

break;

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

设置不使用清单文件

编译exe,在其目录下新建DotLocal.exe.local

运行exe成功劫持dll

Side-by-Side Components(WinSxS)

适用于Windows Server 2003及以后 和 Windows XP及以后的操作系统

在Windows XP SP2以后,Windows引入了side by side执行的概念,这个概念本来是.NET提出来的,但是Windows后来将这个概念集成到操作系统层面上来了。side by side提出不同版本的DLL文件可以同时存在于一个系统里面。

MSDN上对这个WinSxS功能的描述为:

application developers can build isolated applications that are fully self-describing and unaffected by changes to the registry, other applications, or other versions of assemblies running on the system.

Application authors and administrators can use manifests to manage the sharing of side-by-side assemblies after deployment on either a global or per-application basis.

assembies(程序集)在.net中,将exe、dll都看成“程序集(assemble)”,每个程序集(assemble)都附带有一个manifest清单文件

SxS允许二进制文件嵌入manifest文件来表达详细的二进制依赖信息。

当Windows公共控件包comctl32.dll被分裂为多个可以相互并存的版本以后该机制被使用,因而应用程序可以加载正确版本的二进制文件

此后其他的二进制文件也采用相同的方式进行版本管理。VS2005及以后,用MS链接器编出来的应用程序使用SxS机制来定位到正确的C运行时库。

notepad.exe的manifest文件中dependency部分:

查看manifest文件可使用SysinternalsSuite工具集中的sigcheck.exe查看

命令如下

1
sigcheck.exe -m notepad.exe
1
2
3
4
5
6
7
8
9
10
11
12
13

<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="x86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>

notepad.exe加载DLL时系统会从如下路径加载comctl32.dll

C:\Windows\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_41e6975e2bd6f2b2\comctl32.dll

Bypass UAC问题的产生

之前在说DotLoad(.local)解决DLL Hell问题时有提到一点,当应用程序存在 Manifest 时,该技术将不会起作用。

但是经过实验,在Windows 7 ~ Windows 10系统(未测试其他系统)默认设置下,

当可执行文件存在manifest并设置dependency使用SxS机制时,Dotload目录仍然会起作用。

以notepad.exe为例,启动notepad.exe后,使用procmon.exe监控进程文件操作情况

notepad.exe仍然会首先去找.local文件夹,如上图所示,在没有找到.local文件夹后使用SxS加载C:\Windows\winsxs\下的dll

那么如果.local文件夹存在呢,在notepad.exe目录下新建一个notepad.exe.local的文件夹

会发现notepad会去找是否存在

C:\Windows\System32\notepad.exe.local\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_41e6975e2bd6f2b2

如果该文件夹下存在comctl32.dll则notepad.exe会加载该dll而非WinSxS下的dll

(无法启动的原因是我在该目录下放了个自定的DLL没有导出函数,仅用来试验一下效果)

那这种方式怎么导致Bypass UAC的呢?

参考:UACME - UACMETHOD21_SXS/UACMEMETHOD22_CONSENTSXS

参考

Windows Internal 6th 3.10节 DLL Name Redirection

动态链接库重定向技术:http://www.cnblogs.com/zealic/archive/2008/06/16/1222780.html

DLL Redirection : https://msdn.microsoft.com/en-us/library/ms682600(VS.85).aspx

DLL/COM Redirection: https://msdn.microsoft.com/en-us/library/aa375142(v=vs.85).aspx

应用隔离和SxS机制:https://msdn.microsoft.com/en-us/library/dd408052(v=vs.85).aspx

Side by Side 解决方案: http://blog.csdn.net/leehong2005/article/details/8607522

DotLocal和SxS的安全问题:

http://www.hexacorn.com/blog/2015/01/09/beyond-good-ol-run-key-part-23/

Using SxS redirection to gain NT AUTHORITY\SYSTEM privileges:

http://www.kernelmode.info/forum/viewtopic.php?f=11&t=3643&start=110

DLL Search Order Hijacking:

https://attack.mitre.org/wiki/Technique/T1038

DLL Side-loading:

https://www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/rpt-dll-sideloading.pdf