Bypass UAC 方法研究

  1. 0x01 向受保护目录写文件的方法
  2. 0x02 伪装白名单进程
  3. 0x03 Bypass UAC方法
    1. 1. DLL加载顺序劫持
    2. 2. 使用manifest文件进行DLL劫持
    3. 3. 使用WinSxS机制进行DLL劫持
    4. 4. 通过代码注入绕过UAC
    5. 5. 关闭UAC机制
    6. 6. 使用注册表指定程序加载DLL
  4. 0x04 总结

author: Dlive

阅读了UACME部分源码,记录一下其中使用到的Bypass UAC的技术原理。

0x01 向受保护目录写文件的方法

这里的受保护目录指需要管理员权限才能向其中写文件的目录,如C:\windows\system32,若无管理员权限,则向其中复制文件时会弹出UAC窗口

在本项目中向受保护目录写文件主要使用了两种方法

  1. IFileOperation COM对象

    IFileOperation COM对象进行文件操作是可以自动提升权限(AutoElevate)(从标准用户到管理员用户),但是它会检查当前使用该COM对象的进程是否为白名单 进程,仅白名单进程的条件下可以进行自动权限提升。(伪装白名单的方法见0x04)

    在白名单进程中使用IFileOperation COM向受保护目录写文件时不会弹出UAC窗口。

    该项目中实现了使用IFileOperation COM向受保护目录写文件的方法(函数实现位于 pitou.c文件中)

    1
    2
    3
    4
    BOOL ucmMasqueradedMoveFileCOM(
    LPWSTRSourceFileName, //源文件
    LPWSTRDestinationDir//目的目录
    );
  2. Windows更新独立安装程序(WUSA.exe)

    和IFileOperation COM相似,白名单进程可以使用wusa进行向受保护目录写文件的操作。

    可运行如下命令向受保护目录写文件。

    1
    wusa sourcefilename   /extract:%windir%\\system32

    wusa命令的源文件是一个cab格式的文件,该格式是一种压缩包文件格式

    该项目使用ucmCreateCabinetForSingleFile函数实现生成cab压缩包

    1
    BOOL ucmCreateCabinetForSingleFile(LPWSTR lpSourceDll, PVOID ProxyDll, DWORD ProxyDllSize)

    使用ucmWusaExtractPackage函数执行wusa命令,向受保护文件目录解压压缩包

    1
    BOOL ucmWusaExtractPackage(LPWSTR lpCommandLine)

    生成压缩包也可以使用makecab命令生成,第一个参数为需要打包的文件名,第 二参数为生成的压缩包文件名。

0x02 伪装白名单进程

以Windows 7 为例,在Windows 7下会自动执行的白名单进程有explorer.exe, wuauclt.exe,taskhost.exe。由于后两者并不是一直可以运行,如果使用DLL注入来以白名单进程身份执行操作的话,能注入的比较可靠的进程为explorer.exe。(若以挂起的形式启动其他白名单进程,或直接启动不会闪退的白名单进程(如sysprep.exe),然后对其进行注入dll应该也是可以的吧)

但若以该项目中的方法伪装白名单进行,伪装成任何一个白名单进程均可。

该项目中伪装为的进程是explorer.exe。(这里伪装成taskhost.exe.sysprep.exe等都可以)

进程伪装分为两步

  1. 修改PEB中进程信息CommandLine和ImagePathName,实现如下

    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
    35
    VOID supMasqueradeProcess(
    VOID
    )
    {
    SIZE_T sz = 0x1000;
    PPEB Peb = g_ctx.Peb;
    DWORD cch;
    WCHAR szBuffer[MAX_PATH + 1];

    RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
    cch = GetWindowsDirectory(szBuffer, MAX_PATH);
    if ((cch != 0) && (cch <MAX_PATH))
    {

    lstrcat(szBuffer, L"\\explorer.exe");

    g_lpszExplorer = NULL;
    NtAllocateVirtualMemory(NtCurrentProcess(), &g_lpszExplorer, 0, &sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (g_lpszExplorer)
    {
    lstrcpy(g_lpszExplorer, szBuffer);

    RtlEnterCriticalSection(Peb->FastPebLock);

    //修改ImagePathName(可执行文件路径)为c:\windows\explorer.exe
    RtlInitUnicodeString(&Peb->ProcessParameters->ImagePathName, g_lpszExplorer);
    //修改CommandLine为任意信息
    RtlInitUnicodeString(&Peb->ProcessParameters->CommandLine, APPCMDLINE);

    RtlLeaveCriticalSection(Peb->FastPebLock);
    //枚举进程内存中的模块,调用回调函数supxLdrEnumModulesCallback
    LdrEnumerateLoadedModules(0, &supxLdrEnumModulesCallback, (PVOID)Peb);
    }
    }
    }
  2. 修改内存中的进程模块的信息

    首先通过LdrEnumerateLoadedModules枚举进程内存中的模块,对每个模块调用如下回调函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    VOIDNTAPI supxLdrEnumModulesCallback(
    _In_PCLDR_DATA_TABLE_ENTRYDataTableEntry,
    _In_PVOIDContext,
    _In_OUTBOOLEAN *StopEnumeration
    )
    {
    PPEB Peb = (PPEB)Context;

    //比较模块基址确定模块是否为UACDemo.exe
    if (DataTableEntry->DllBase == Peb->ImageBaseAddress)
    {
    //修改模块中的FullDllName(可执行程序的完整路径)c:\windows\explorer.exe
    RtlInitUnicodeString(&DataTableEntry->FullDllName, g_lpszExplorer);
    //修改模块中BaseDllName(UACDemo.exe)为explorer.exe
    RtlInitUnicodeString(&DataTableEntry->BaseDllName, EXPLORER_EXE);
    *StopEnumeration = TRUE;
    }
    else
    {
    *StopEnumeration = FALSE;
    }
    }

0x03 Bypass UAC方法

该工程中Bypass UAC多数利用Windows中可自动提升权限(AutoElevate)的应用。

这些应用满足以下一些条件,应用是一个Windows可执行文件,它必须由发行者和Microsoft签名,它必须位于几个安全目录之一:%systemroot%/system32以及它的绝大多数子目录,%systemroot%/ehome,以及%ProgramFiles%下面的部分目录(包括Windows Defender, Windows Journal, Windows Media Player)。(但是在方法14中将taskhost拷贝到了c:\Windows下后仍然能够通过自动提升权限绕过UAC,难道c:\windows也是个安全目录(白名单)?根据瀚海源在Sys360大会上的演讲,windows目录确实是白名单,但是windwos internals里却没有写经验证,windows目录及其部分子目录如system32\,syswow64)确实是白名单

大多数应用需要满足manifest文件中通过autoElevate元素请求了自动权限提升,mmc.exe是一个特例,它是否自动提升权限取决于它要加载哪些系统管理加载件(msc)。

C:\windows\system32\sysprep\sysprep.exe的manifest文件如下,声明了以管理员权限运行(requireAdministrator),并且请求自动提升权限(<autoElevate>true</autoElevate>)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
......(省略部分内容)
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<autoElevate>true</autoElevate>
</asmv3:windowsSettings>
</asmv3:application>
...

COM对象可以在它们的注册表键值中请求管理员权限,做法是创建一个名为Elevate的子键,其中包括一个名为Enabled的REG_DWORD值,其值为1。COM对象本身和把它实例化的可执行文件都必须满足Windows可执行文件的要求。

Windows对AutoElevate应用的路径校验的实现位于appinfo.dll(appinfo service)中,对签名的校验位于consent.exe中。

1. DLL加载顺序劫持

DLL劫持中最常见的一种劫持方法,即在程序所在目录放置要劫持的DLL,程序启动时首先从本目录加载DLL,从而导致DLL劫持。

该工程中的方法01就使用了这种方式进行DLL劫持进而提升权限。

方法01 Bypass UAC流程

  1. 生成ellocnak.msu,此文件是一个cab格式的文件,内容为ntwdblib.dll文件(该文件为程序生成的加密Payload),文件放置在用户临时目录下

  2. 通过之前介绍的WUSA将ellocnak.msu解压到system32目录下
    cmd.exe /c wusa %temp%\ellocnak.msu /extract:%windir%\system32

  3. 运行C:\windows\system32\cliconfig.exe,进行DLL劫持

该方法劫持里cliconfig.exe对ntwdblib.dll的加载。

该项目中大部分Bypass UAC的方式都是这种DLL劫持的方法,只是劫持的DLL和EXE有所不同。

2. 使用manifest文件进行DLL劫持

利用manifest进行Bypass UAC的方式是微软在修复一次由DLL加载顺序劫持导致的Bypass UAC时自己暴露出来的一种Bypass UAC的可行方案。

该工程中方法14使用了这种Bypass UAC的方法。

在Windows中有些可执行程序没有内置的manifest,假设这个程序叫test.exe,如果攻击者在该可执行程序目录下新建一个test.exe.manifest并在manifest文件中指定file元素,则test.exe执行时会加载file元素中loadFrom属性指定的DLL(loadFrom的dll不能在KnownDlls中)。

方法14 Bypass UAC流程:

  1. 程序会在C:\windows下写一个taskhost.exe(从C:\windows\system32\taskhost.exe下面拷贝过去的)和taskhost.exe.manifest文件,在sysprep/下写一个cryptbase.dll(因为在C:\windows\system32\下本身存在cryptbase.dll,为了不影响系统功能,需要将攻击者自定义内容的cryptbase.dll拷贝到其他地方, 为什么不把taskhost.exe.manifest写在C:\windows\system32下,因为taskhost.exe在同时存在manifest指定的dll和本目录下的dll时,会加载后者)

  1. manifest文件内容如下,由于taskhost.exe无内置清单文件如上图所示,所以会从manifest中指定的路径加载DLL即C:\Windows\system32\sysprep\cryptbase.DLL(该DLL的名称可变,但file标签下name的值需要时taskhost.exe会自动加载的dll名称,即cryptbase.DLL)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<assembly
xmlns="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
manifestVersion="1.0"
>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevellevel="requireAdministrator"uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<autoElevate>true</autoElevate>
</asmv3:windowsSettings>
</asmv3:application>
<fileloadFrom="C:\Windows\system32\sysprep\cryptbase.DLL"name="cryptbase.DLL" />
</assembly>

3. 使用WinSxS机制进行DLL劫持

该工程中方法18使用该方法绕过UAC。

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

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

当Windows公共控件包comctl32.dll被分裂为多个可以相互并存的版本以后该机制被使用,因而应用程序可以加载正确版本的二进制文件。此后其他的二进制文件也采用相同的方式进行版本管理。

C:\Windows\System32\sysprep\sysprep.exe的manifest文件如下,其中定义了dependency字段,这字段就是用来表达详细的二进制依赖信息的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
......
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="amd64"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>

</assembly>
......

sysprep.exe在运行时会加载dependency设置的dll,比如name“Microsoft.Windows.Common-Controls”的对应的dll为comctl32.dll,sysprep.exe会加载C:\windwos\winsxs中的comctl32.dll,而不是加载system32下的dll。

但是在加载winsxs下的dll之前,windows会先应用Dotlocal机制去查找dll,这个机制也是MS推出的为了解决兼容性问题的一个机制,exe会首先检查当前目录下有没有sysprep.exe.local的文件夹,如果有则在该文件夹中寻找dll

C:\Windows\System32\sysprep\sysprep.exe.local\amd64microsoft.windows.common-controls6595b64144ccf1df6.0.7601.18837none_fa3b1e3d17594757\comctl32.dll

若没有则会从winsxs下寻找dll

C:\Windows\winsxs\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.18837_none_fa3b1e3d17594757\comctl32.dll

方法18 Bypass UAC流程

  1. 使用IFileOperation COM在C:\windows\system32\sysprep\下新建文件夹

sysprep.exe.local\amd64microsoft.windows.common-controls6595b64144ccf1df6.0.7601.18837none_fa3b1e3d17594757\

  1. 使用IFileOperation在该文件夹下写入comctl32.dll

  2. 运行sysprep.exe进行DLL劫持

该工程中方法20也通过WinSxS绕过的UAC。

不同的是,方法20 DLL劫持的对象为system32\consent.exe,consent.exe是一个由svchost进程启动的,用来验证用户启动的程序是否需要做UAC控制的一个程序。用户若要以管理员权限运行某程序,且该程序不是AutoElevate的程序,则consent.exe会生成UAC窗口等待用户确认。

因为consent.exe由svchost程序启动,其权限为System权限,攻击者通过WinSxS劫持consent.exe加载comctl32.dll,comctl32.dll也是以System权限运行,即攻击者可以直接拿到System权限。

4. 通过代码注入绕过UAC

该工程中方法16使用了这种BypassUAC方法。

具体操作流程如下:

  1. 将payloadDll使用IFileOperation拷贝C:\Windows\system32\wbem\netutils.dll‘

  2. 将C:\Windows\system32\credwiz.exe 拷贝到Temp目录下命名为oobe.exe

  3. 将Temp\oobe.exe使用IFileOperation拷贝到C:\Windows\system32\wbem\oobe.exe

  4. 启动credwiz.exe(以挂起模式CREATE_SUSPENDED启动)并向其进程注入shellcode(向credwiz中注入的代码是ucmElevatedLaunchProc函数的代码)

  5. ucmElevatedLaunchProc中执行ShellExec (以runas方式执行)启动C:\Windows\system32\wbem\oobe.exe,该可执行程序存在DLL劫持,C:\Windows\system32\wbem\netutils.dll得以运行,进而绕过UAC

5. 关闭UAC机制

该工程中方法9使用了这种Bypass UAC的方法。该方法第一次被使用是在一个叫Simda的恶意软件中。

这个方法有一个缺点,需要重启系统才能成功关闭UAC。

该方法利用微软没有公开的ISecurityEditor COM对象,和IFileOperation COM对象类似,该对象是一个AutoElevate的COM对象,在白名单进程中使用可以自动提升权限。

这个对象可以用于修改注册表的访问权限,攻击者可以伪装白名单进程然后修改如下注册表权限为可写

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System

然后设置EnableLUA为0 (EnablelUA REG_DWORD 0)即可关闭UAC。

方法9 Bypass UAC流程:

  1. 使用ISecurityEditor修改上述注册表权限为可写

  2. 新建/修改注册表键EnableUA的值为0

  3. 重启系统,成功关闭UAC

6. 使用注册表指定程序加载DLL

该工程中方法3使用了这种Bypass UAC的方法

该方法同样使用ISecurityEditor COM,修改的注册表项为,将其访问权限改为可写

1
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows   NT\CurrentVersion\Image File Execution Options

然后在该注册表项下新建子项cliconfg.exe

在子项下新增键值对

1
2
GlobalFlag REG_DWORD 0x0000100   
VerifierDlls REG_SZ Hibiki.dll

VerifierDlls的值为dll的名字,该dll为攻击者自定义dll,该DLL需要放置在C:\Windows\system32下。VerifierDlls中存储的DLL会被AppVerfier(应用程序检验器)使用。

通过WUSA将攻击者自动以DLL解压至C:\windwos\system32\Hibiki.dll下,然后运行cliconfg.exe即可在运行时自动加载Hibiki.dll,因为cliconfg.exe是AutoElevate的exe,所以Hiibiki.dll会以管理员权限运行,即绕过了UAC。

0x04 总结

Bypass UAC方法编号 使用的方法 目标exe/注册表 劫持的DLL 有效版本
1 WUSA,DLL劫持 system32\cliconfg.exe ntwdblib.dll Win7(7600)-Win10(10147)
2 IFileOperation,DLL劫持 system32\sysprep\sysprep.exe actionqueue.dll Win7(7600)-Win8.1(9600)
3 ISecurityEditor,WUSA,修改注册表加载DLL sysrem32\cliconfg.exe Image File Execution Options 攻击者自定义dll Win7(7600)-Win10(10147)
4 WUSA,IFileOperation,DLL劫持 system32(新建)\winsat.exe powerprof.dll, Win7(7600)-Win10(10548)
5 IFileOperation,DLL劫持 system32\sysprep\sysprep.exe dbgcore.dll Win10(10240)-Win10(10565)
6 IFileOperation,DLL劫持 system32\sysprep\sysprep.exe cryptbase.dll Win7(7600)-Win8.1(9600)
7 IFileOperation,DLL劫持 system32\inetsrv\InetMgr.exe MSCOREE.DLL Win7(7600)-Win10(14376)
8 IFileOperation,DLL劫持 system32\oobe\setupsqm.exe wdscore.dll Win7(7600)-Win10(10558)
9 ISecurityEditor,修改注册表关闭UAC UAC设置所在注册表 Win7(7600)-Win10(10147)
10 WUSA,DLL劫持 system32\migwiz\migwiz.exe wdscore.dll Win7(7600)-Win10(10147)
11 IFileOperation,DLL劫持 system32\cliconfg.exe ntwdblib.dll Win7(7600)-Win10(14316)
12 IFileOperation,DLL劫持 system32\inetsrv\InetMgr.exe SLC.dll Win7(7600)-Win10(14316)
13 IFileOperation,DLL劫持 system32\sysprep\sysprep.exe unbcl.dll Win7(7600)-Win10(14371)
14 IFileOperation,利用manifest文件进行DLL劫持 system32\taskhost.exe cryptbase.dll Win7(7600)-Win10(14371)
15 IFileOperation,DLL劫持 system32\mmc.exe EventVwr.msc elsext.dll Win7(7600)-Win10(14316)
16 IFileOperation,DLL劫持,代码注入 system32\credwiz.exe netutils.dll Win7(7600)-Win10(10548)
17 IFileOperation,DLL劫持 system32\mmc.exe rsop.msc wbemcomn.dll Win7(7600)-Win10(14371)
18 IFileOperation,SxS,DLL劫持 system32\sysprep\sysprep.exe comctl32.dll Win7(7600)-未修复
19 IFileOperation system32\pkgmgr.exe(/n参数为AutoElevate,例pkgmgr /n:123) dismcore.dll Win7(7600)-未修复
20 IFileOperation,SxS,DLL劫持 System32\consent.exe comctl32.dll Win7(7600)-未修复