BypassUAC

本篇主要介绍如何以 ICMLuaUtil 方式BypassUAC,主要内容如下:

  • 过掉 UAC 提示框的方法总结
  • UACME项目
  • 什么类型的 COM interface 可以利用?
  • 如何快速找到系统中的所有可利用的 COM 组件?
  • 定位 ICMLuaUtil 的虚函数表vftable
  • 如何调用 ICMLuaUtil.ShellExec 执行命令?
    • C++ version
    • CSharp version
      • 两个注意点
  • DLL形式
    • C++ dll 导出函数方式
    • C# dll 导出函数方式
      • 一个坑
  • 值得研究的 C2 推荐

过掉 UAC 提示框的方法总结

这里主要说的是 dll 的形式,通过上面的实操,可以发现有两种方法:

  • 基于白名单程序绕过UAC
  • 伪装进程 PEB 绕过UAC
  • 无文件技术

伪装进程的方式其实也可以算做借助了白名单,但是没有直接调用白名单进程,所以单独列出来了。

基于白名单程序绕过 UAC

有些系统程序是直接获取管理员权限,而不会触发 UAC 弹框,这类程序称为白名单程序,例如:slui.exewusa.exetaskmgr.exemsra.exeeudcedit.exeeventvwr.exeCompMgmtLauncher.exerundll32.exeexplorer.exe等等。

常见的利用方式有:

  • DLL注入(RDI技术),一般注入到常驻内存的可信进程,如:explorer
  • DLL劫持,常和注册表配合使用达到劫持目的

伪装进程 PEB 绕过 UAC

上面在利用 COM 接口的 ShellExec 执行命令的时候,因为执行该操作的进程身份是不可信的,所以会触发 UAC 弹窗。为了能够迷惑系统,通过修改 PEB 结构,让系统误认为这是一个可信进程,伪装的可信进程可以是 calc.exerundll32.exeexplorer.exe 等。

利用火绒剑查看进程信息,可以看到已经识别为可信进程了:

关于 PEB 的结构,可以参照 这里

无文件技术

“无文件攻击”是一种攻击策略,其出发点就是避免将恶意文件放在磁盘上,以逃避安全检测。无文件四种攻击形式:

  1. 恶意文档

    比如:在 word 中加入恶意的宏代码实现命令执行,又或者邮件中。

  2. 恶意脚本

    常用的脚本引擎:powershell.exe,cscript.exe,cmd.exe 和 mshta.exe,同样不生成恶意二进制文件。

  3. 恶意本地程序交互

    例如:rundll32.exewmi等,详细参考 这里

  4. 恶意内存代码

    直接生成纯shellcode,通过其他方式加载到内存执行。

UACME 项目

项目地址:https://github.com/hfiref0x/UACME

项目总结了 50 多种绕过 UAC 的方式,并且列出具备 auto-elevate 能力的 UAC 白名单程序或接口。

利用方式主要可以分为两大类:

  1. 各类 UAC 白名单程序的 DLL 劫持(Dll Hijack
  2. 各类提升权限的 COM 接口利用(Elevated COM interface

项目的主程序为 Akagi,其中包含了所有的method,使用vs2019 本地编译后可以使用 akagi32 41 或者 akagi64 41 启动程序,41这个指的是 README 中描述的方法索引,运行后可以直接得到管理员权限的 cmd 窗口。

项目的 Source 目录存储的是所有子项目的源码,其中 Source/Shared 存放的是被所有子项目共同引用的一些函数,本篇主要利用 AkagiYuubari这两个 Project 来探究一下如何利用 COM 接口提升权限。

什么类型的 COM interface 可以利用?

以项目中索引为 41 的方法为例:

1
2
3
4
5
6
7
8
9
Author: Oddvar Moe
Type: Elevated COM interface
Method: ICMLuaUtil
Target(s): Attacker defined
Component(s): Attacker defined
Implementation: ucmCMLuaUtilShellExecMethod
Works from: Windows 7 (7600)
Fixed in: unfixed 🙈
How: -

该方法的目标接口是 ICMLuaUtil,对应Akagi 项目中具体实现函数为 ucmCMLuaUtilShellExecMethod,在项目中的methods/api0cradle.c 文件中可以找到该方法的定义:

观察发现这里利用的是 CMSTPLUA 组件的 ICMLuaUtil 接口。

我的测试系统 Windows 10 (1909),使用OleViewDotNet 工具可以查看系统中的 COM 接口属性信息,注意需要以管理员权限运行。

打开 CLSIDs 窗口搜索cmstplua,可以快速定位该组件:

右键查看 CMSTPLUA 组件的 Elevation 属性:

这里的 EnabledAuto Approval值都是 True 表示这个组件可以用来绕过 UAC 认证,这是第一点。

第二点是目标接口 ICMLuaUtil 需要有一个可以执行命令的地方,通过在 CISIDs 窗口鼠标悬浮在 ICMLuaUtil 上,可以看到该接口对应的二进制文件为cmlua.dll

虚函数偏移为 cmlua.dll+0x6360,通过IDA 打开该系统文件 (c:\windows\system32\cmlua.dll),跳到虚函数表的位置,可以看到ICMLuaUtil 接口的虚函数表:

摘出来看接口函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
01 QueryInterface(_GUID const &,void * *)
02 AddRef(void)
03 Release(void)
04 SetRasCredentials(ushort const *,ushort const *,ushort const *,int)
05 SetRasEntryProperties(ushort const *,ushort const *,ushort * *,ulong)
06 DeleteRasEntry(ushort const *,ushort const *)
07 LaunchInfSection(ushort const *,ushort const *,ushort const *,int)
08 LaunchInfSectionEx(ushort const *,ushort const *,ulong)
09 CreateLayerDirectory(ushort const *)
10 ShellExec(ushort const *,ushort const *,ushort const *,ulong,ulong)
11 SetRegistryStringValue(int,ushort const *,ushort const *,ushort const *)
12 DeleteRegistryStringValue(int,ushort const *,ushort const *)
13 DeleteRegKeysWithoutSubKeys(int,ushort const *,int)
14 DeleteRegTree(int,ushort const *)
15 ExitWindowsFunc(void)
16 AllowAccessToTheWorld(ushort const *)
17 CreateFileAndClose(ushort const *,ulong,ulong,ulong,ulong)
18 DeleteHiddenCmProfileFiles(ushort const *)
19 CallCustomActionDll(ushort const *,ushort const *,ushort const *,ushort const *,ulong *)
20 RunCustomActionExe(ushort const *,ushort const *,ushort * *)
21 SetRasSubEntryProperties(ushort const *,ushort const *,ulong,ushort * *,ulong)
22 DeleteRasSubEntry(ushort const *,ushort const *,ulong)
23 SetCustomAuthData(ushort const *,ushort const *,ushort const *,ulong)

其中第 10 个函数 ShellExecIDA中看到该函数调用了 ShellExecuteEx 这个 Windows API 实现了命令执行:

通过对 ICMLuaUtil 接口的分析,可以看出可以用来 BypassUAC 执行命令的 COM 组件需要有两个特点:

  1. elevation属性启用,且开启Auto Approval
  2. COM组件中的接口存在可以命令执行的地方,例如 ICMLuaUtilShellExec

如何快速找到系统中的所有可利用的 COM 组件?

除了通过上面的方式在 OleView 中手动去找,还可以通过 UACMe 项目提供的 Yuubari 工具快速查看系统 UAC 设定信息以及所有可以利用的程序和 COM 组件,使用方法如下:

使用 VS2019 加载 Yuubari,生成后会得到二进制文件UacInfo64.exe,运行后在同目录生成一个log 文件记录所有输出结果:

从这里面可以找到所有的 Autoelevated COM objects,包括CMSTPLUA 组件的信息:

定位 ICMLuaUtil 的虚函数表 vftable

通过分析 UACMe 中的 ucmCMLuaUtilShellExecMethod 实现可以知道想要利用 COM 接口,需要知道这几个东西:

  • 标识 COM 组件的GUID,即CLSID
  • 标识 interfaceGUID,即IID
  • 该接口的虚函数表,主要用来找到 ShellExec 的函数偏移

前两个可以很容易找到,虚函数表可以通过 OleView 提示的虚函数表位置偏移找到,这里再说一种通用的方法,完全利用IDA

第一步,用 IDA 打开 cmlua.dll
第二步,在左侧函数列表中搜索 destructor 或者 constructor,双击后跳转后,上下找找可以看到调用vftable 的地方:

双击跳转到变量定义位置,就可以找到虚函数表!

参考:Get interface definition of undocumented COM objects

如何调用 ICMLuaUtil.ShellExec 执行命令?

c++ version

代码是从 UACMe 中摘出来的,放在了 github 上。

代码地址:BypassUAC

如果直接把 ucmCMLuaUtilShellExecMethod 这个函数直接摘出来,会发现还是会弹 UAC 的窗:

vs2019 中可以对 Akagi 项目调试,项目属性中设置命令参数为41

直接在函数 ucmCMLuaUtilShellExecMethod 的地方下断:

通过分析函数调用链,发现 ucmMain 在调用对应方法之前先调用了 supMasquradeProcess 这个函数。

该函数负责进行 PEB 的伪装,将自己的进程信息伪装成为 c:\windows\explorer.exe 这个系统的可信进程,这样才能绕过 UAC 认证窗口,所以在使用 COM 组件提权之前需要先伪装一下进程才可以:

这种方式,因为修改的是自己的进程信息,并不是修改其他进程,所以一般杀软、AV 是不会拦的。

CSharp version

代码摘自 Moriarty2016p0wnedShell

代码地址:BypassUAC_csharp

两个注意点

C# 版本的代码中需要注意 ICMLuaUtil 接口的定义,其继承自IUnKnown,该接口定义函数如下:

1
2
3
IUnknown::AddRef
IUnknown::QueryInterface
IUnknown::QueryInterface

所以在定义 ICMLuaUtil 的时候,有以下两点需要注意:

  1. 指明继承自 IUnKnown 接口;
  2. 继承的函数不需要加上,C#会自动添加;

关于 C# 接口的知识,可以从 这里 了解更多。

DLL 形式

如何使用?

dll可以使用系统可信进程 rundll32.exe 进行加载,这样也不需要调用MarquradePEB

C++ dll 导出函数的方式

代码地址:BypassUAC_Dll

导出的函数为 BypassUAC,导出方式直接新建一个def 文件,格式参考 这里,内容如下:

1
2
3
LIBRARY	BypassUAC
EXPORTS
BypassUAC

生成之后通过 CFF Explorer 查看导出表:

利用 rundll32.exe .\BypassUAC_Dll.dll,BypassUAC 命令测试后,bypass成功!

C# 导出 dll 函数的方式

代码地址:BypassUAC_Dll_csharp

C#导出 dll 函数的方式有两种:

  1. 使用 DllExport 这个 NuGet
  2. 通过 IL 反编译的方式

DllExport

默认 C# 导入其他库函数,可以使用 [DllImport],但是不支持[DllExport],通过NuGet 包管理器安装 DllExport 这个包可以实现这个功能。

vs 中可以对指定项目安装这个包:

安装之后,直接使用 [DllExport] 导出 BypassUAC 函数即可:

重新生成 dll 文件,在 CFF 中查看,已经导出成功:

IL 反编译

如果了解 Java 的,java文件首先编译成 class,然后交给JVM 去解释成机器码。.net为了跨平台,这里类似,同样有一个中间语言的文件,但不是 class 了,而是IL

通过修改 IL 文件,也可以导出 dll 函数。

首先去除 [DllExport] 后将 dll 代码编译,编译后的 dll 文件是看不到 Export Directory 的。

然后 ildasmdll文件反编译成 il 文件,命令如下:

1
ildasm BypassUAC_Dll_csharp.dll /out=BypassUAC_Dll_csharp.il

打开生成的 BypassUAC_Dll_csharp.il 文件,找到需要导出的目标函数BypassUAC,在函数开头处添加如下代码:

1
.export [1]

保存后,需要使用 ilasm 再把 il 编译成 dll 文件,这里遇到一个坑,如果按照如下命令进行编译:

1
ilasm BypassUAC_Dll_csharp.il /dll /out=BypassUAC_Dll_csharp_exp.dll

发现使用 rundll32.exe .\BypassUAC_Dll_csharp_exp.dll,BypassUAC 运行后没有任何反应。

BypassUAC 函数开头处添加一个 MessageBox 弹窗,再次运行弹框之后,附加到 windbg 调试,让程序再次跑起来,运行结束后在 windbg 中可以看到:

进程加载的 cmlua.dll 文件并不是 system32 目录,而是 SysWOW64 的,SysWOW64放的是 32 位系统文件,程序为什么去加载的是 32 位的,使用 CFF 看一下生成的 dll 文件类型:

问题的根源就是这里,利用 IL 转之前的 dll64位的,转之后变成了 32 位的,解决方法很简单,使用 ilasm 的时候添加一个 /X64 参数就可以了:

1
ilasm BypassUAC_Dll_csharp.il /dll /X64 /out=BypassUAC_Dll_csharp_exp.dll

这样就可以成功的 BypassUAC 了。

值得研究的 C2 推荐

  • Python

    • Empire,官方已经不维护了不推荐用,但是值得研究借鉴
    • Pupy,很适合玩 Python 的研究
    • SILENTTRINITY,利用的 .net DLR 方式实现,动态加载不落地
    • PoshC2
  • .NET

其他参考




root@kali ~# cat 重要声明
本博客所有原创文章,作者皆保留权利。转载必须包含本声明,保持文本完整,并以超链接形式注明出处【[Techliu](https://scriptboy.cn)】。查看和编写文章评论都需翻墙,为了更方便地获取文章信息,可订阅[RSS](https://feeds2.feedburner.com/techliu),如果您还没有一款喜爱的阅读器,不妨试试[Inoreader.](https://www.inoreader.com)。
root@kali ~# Thankyou!

⬆︎TOP