- 本文转载于跳跳糖安全社区,原文链接为https://tttang.com/archive/1715/
0x00 前言
任何需要管理员访问令牌的程序都必须征得同意(即UAC弹窗),但是存在一个意外,即父进程和子进程之间存在关系,即子进程从父进程中继承访问令牌,这是UAC 绕过的基础。本文将选择UACME中的3种不同的ByPass UAC的例子,为各位讲解ByPass UAC的原理。然后分析UAC的基本基本原理,继而寻找ByPass UAC的通用解决策略,本文行文仓促,如有错误,请各位积极指正。
0x01 常见的ByPassUAC 原理
UACME项目中,主要的ByPass UAC方法主要有Shell API,Dll Hiject,Elevated COM interface,Whitelisted component。其中,比较常见的就是前三个。本文将根据UACME3.5.6这个版本的23,41,53号方法进行讲解。
UACME的第23号方法的类型是Dll Hijack。除了23号方法以外,UACME中同处于Dll Hijack还有18号,22号,26号,30号,37号,39号方法,其中UACME的23号方法主要是利用pkgmgr.exe实现ByPass UAC。其在Windows7下可以被成功利用,实现的方法是ucmDismMethod函数。
在编译完UACME,设置好参数(akagi.exe 23 calc.exe)之后,在MethodsManagerCall函数处下断。可以看到ucmDismMethod会依次调用ucmxGenericAutoelevation(),和ucmxDisemer()函数。其中ucmxGenericAutoelevation()主要是为了将所谓的proxy dll释放到C:\Users\kent\AppData\Local\Temp\dismcore.dl!。然后在利用COM接口——CLSID_FileOperation将文件复制到”C:\Windows\system32\dismcore.dll”。值得注意的是,如果程序是32位的话,会将dismcore.dl!释放到C:\Windows\SysWow64\目录下,如果程序是64位的话,则会释放到C:\Windows\system32。
接着调用ucmxDisemer()拉起一个pkgmgr.exe进程,从此完成ByPassUAC
为什么只需要短短两步就可以完成ByPass UAC?我们使用ProcessMon监控当执行Akagi.exe 23 calc.exe
之后,发生了什么。对ProcessMon做出如下配置。
可以清楚的看到,当akagi.exe拉起pkgmgr.exe之后,pkgmgr.exe会创建dism.exe进程。但是dism.exe又和有什么关系呢?我们试着带着上帝视野的角度看看当时Leo Davidson是怎么找到这个ByPass UAC方法的。
首先我们需要把握两个关键点,第一这是Dll HiJack方法,所以在这个方法中,关于加载Dll的行为一定要密切关注,第二,其所利用到的组件是DismCore.dll。综上两点,我们可到一个关键点,即dism.exe进程加载DismCore.dll的时候发生了ByPass UAC利用。并且从ProcessMon的细节中,也验证了我们的想法。
既然要加载dll,抛去那些复杂的注入的方式,常见的加载dll的函数无非就是LoadLibrary相关的函数,我们可以通过ProcessMon提供的Stack功能查看调用情况。很显然,dism.exe使用了LoadLibraryW加载DismCore.dll。
分析dism.exe,显然,可以将目标定位到CDismWrapper::FindAndLoadDism函数,在CDismWrapper::FindAndLoadDism()中,会调用CreateObjectFromDLL(),然后调用LoadLibraryW加载DismCore.dll,我们只需要覆盖原始的DismCore.dll,那么dism.exe就会加载我们创建的DismCore.dll,即可完成ByPassUAC。
UACME的第41号方法类型是Elevated COM interface,同属于Elevated COM interface的还有27号,42号,43号,48号,49号,50号,51号方法。鸡哥在从项目中看BypassUAC和BypassAMSI以及https://pingmaoer.github.io/2020/07/09/BypassUAC方法论学习/已经描述了Elevated COM interface的原理,即就是通过具有自动提权的COM接口对应的Dll创建进程实现ByPass UAC。所以使用这个方法需要两个条件。
- 第一:COM的Elevation的Enabled属性为True以及Auto Approval为True
- 第二:接口具有执行的功能。
除此以外,UACMe的Yuubari项目还可以帮助寻找可以被利用的COM组件。具体在BypassUAC方法论学习也有相关介绍。
UACME的53号方法需要在windows10下使用,其类型为Shell API,同属于Shell API类型的还有24号,25号,29号,33号,34号方法。这个类型下有通过修改注册表,通过环境变量,和Shell 劫持实现的,其中大多数通过修改注册表实现。实现的方法是ucmShellRegModMethod。
在设置好调试参数之后,在ucmShellRegModMethod处打个断点,可以看到ucmShellRegModMethod主要在HKCU主键下的\Folder\shell\open\command路径下创建一个符号链接,值就是传入的第三个参数,也就是需要ByPass UAC的程序。然后通过ShellExecuteEx起sdclt.exe进程,完成ByPass UAC之后做好清理即可。
首先,将ProcessMon设置成如下状态,可以看到akagi.exe首先会拉起sdclt.exe,这是已知的,然后添加参数“/name Microsoft.BackupAndRestoreCenter”,拉起“C:\Windows\System32\control.exe”进程。
接着,将ProcessMon设置成如下配置,我们看一下直接创建control.exe进程以及通过ByPass UAC,打开注册表HKCU\Software\Classes\Folder\shell\open\command的差异。如图红色框内,打开注册表的结果是NAME NOT FOUND,但是在绿色方框内,则显示REPARSE。然而我们也注意到在control.exe访问之前,Akagi.exe以及事先设置好了值进去。
0x02 UAC原理和逆向分析
了解过UAC工作原理的师傅都知道,当调用类似于CreateProcess的进程创建函数之后,通过RPC过程调用appinfo文件中的RAiLaunchAdminProcess函数,appinfo主要用于UAC权限验证,如果符合特定的条件,则自动将进程的完整性级别提升到High,否则的话,则会通过UAC弹窗请用户选择。
说到完整性级别,windows操作系统的进程完整性级别一共有4个,分别是Low(低完整性级别),Medium(中完整性级别),High(高完整性级别),System(系统)。在标准模式下,程序一般都运行在Medum级别,等到UAC提升之后,才会提升到High完整性级别。
在RAiLaunchAdminProcess()中,首先,会调用CheckElevationEnabled检查是否启用了UAC。
然后调用AiCheckSecureApplicationDirectory检查是否在文件路径是否在所有的SecureApplicationDirectory,其实就是比较几个系统路径,例如C:\Program Files\
,C:\Windows\
,C:\Windows\System32
等等。
AiCheckSecureApplicationDirectory()函数第一个参数为文件路径,第二个参数是一个标志,如果flag_path为0表示不在可信路径中,如果flag_path 为0x4000,表示在Windows Denfer目录中,或者如果为0x6000,表示在Windows System32目录中。
接着调用AiIsEXESafeToAutoApprove函数,该函数的目的是为了检查文件是否有自动提升标志的程序,或者是白名单程序。在函数中,会调用 AipCheckFusion和 AipIsAutoApprovalEXE函数。 AipCheckFusion函数是通过检查文件中autoElevate标志是否为True来判断是否可以自动提升。而AipIsAutoApprovalEXE是检查白名单路径。以及检查某些特殊的mmc文件。
接着调用AiCheckLUA()函数,AiCheckLUA()函数适用于判断是否进行UAC弹窗,该函数会传入多个参数,包括:UAC级别,可信目录标记(我自创的说法),父进程Token句柄,桌面句柄,命令行等参数。
并调用AiLaunchConsentUI函数,在AiLaunchConsentUI函数中,调用AipGetElevationPromptType通过注册表行为,确定UAC的行为,最终,会通过 AiLaunchProcess函数,拉起一个 consent.exe进程,传入参数主要有Pid,一个有关进程参数路径的结构体,该结构体的初始化可以再 AipSetFixedParams函数中进行。除此以外,还将appinfo服务对应的进程的令牌复制给了consent.exe进程,这样consent.exe进程便拥有了高权限。
拉起consent.exe进程之后,该进程处于暂停状态,需要ResumeThread唤醒,之后便出现UAC弹窗,此时appinfo服务端便处于等待状态,知道用户选择是or否。如果返回值为0,则会将句柄传递出来。这就是是 AiCheckLUA最后一个参数,带回来的是高权限Token句柄。
最后拉起指定的程序,完成UAC。
0x03 ByPass UAC通用检测策略
根据前言,我们了解到ByPass UAC的基础是访问令牌的继承关系,主要得到了异常的访问令牌的进程关系,就可以认定这是一个ByPass UAC。
首先,我们需要知道一个进程是否以完整性级别(TokenElevationTypeFull)运行,即就是进程的权限是否被提升。如果进程权限都没有被提升,那么肯定就不存在ByPass UAC。
如下GetProcessEleation函数
是《windows核心编程》一书中介绍的能够返回提升类型和是否以管理员运行的辅助函数。
其中,我们关注的函数有两个,一个是OpenProcessToken
函数和GetTokenInformation
函数,OpenProcessToken
函数是为了获取进程的Token,而GetTokenInformation
函数获取进程Token信息,通过传入不同的TokenInformationClass信息获取不同的Token信息。
当传入的是TokenElevationType类型,则GetTokenInformation返回值主要有三个。
- TokenEvevationTypeDefault:进程以默认用户运行,或者UAC被禁用
- TokenEvevationTypeFull:进程权限被成功提升
- TokenEvevationTypeLimited:进程以受限权限运行
所以大致只需要通过上述两个API函数即可获取进程权限是否被提升。
然后检查当前进程是否具有微软的数字签名,因为部分微软自带的程序可以不经过UAC弹窗从而自动获取高完整性级别。
首先利用WinVerifyTrust函数(在VerifyEmbeddedSignature函数中)判断是否可信,接着获取Pe文件是否具有MicroSoft的签名信息。
获取文件签名信息的代码可以从https://github.com/konstantin89/windows-pe-signature-verifying处获取。然后比较签名人是否是Microsoft Windows
即可。
|
|
接着判断父进程路径,如果父进程是属于白名单或者具有自动提升标志的,则异常,否则,在检查一下父进程的完整性级别。
通过对AppInfo的逆向分析发现,UAC会针对部分Pe文件进行自动的提权,而大部分ByPass UAC都会利用到这些所谓的白名单文件,所以我们可以针对性的检测这些白名单文件。
AppInfo!AipCheckFusion函数用来检测Pe文件中是否具有“autoElevate”标志。如果为True,则可以免弹窗进行UAC。直接把IDA中的反汇编结果Copy下来,改改就行了。
在AppInfo!AipIsAutoApprovalEXE函数,其实就是比较文件是否是白名单程序。
除此以外呢,还需要检测父进程的完整性即可,只需要调用GetProcessEleation函数即可。
在R0层呢,只需要通过PsSetCreateProcessNotifyRoutineEx回调获取进程,及其父进程的Pid和ProcessName即可。
并通过事件,控制向R3发送数据。首先在DriverEntry中创建一个事件。
当捕获到进程创建,获取完所需的数据之后,通过KeSetEvent将R3程序阻塞。就相当于R0驱动通知了一下R3的程序,有数据过去了,你准备接收一下,这样,R3程序就会接收R0传递的数据。
0x05 参考文献
大家可以在https://www.bilibili.com/video/BV1Fr4y1Q7E8/观看ByPass UAC检测视频。
参考文章: