【UAC】Windows UAC 原理浅析
UAC理解
簡而言之
用戶賬戶控制 (User Account Control) 是Windows Vista (及更高版本操作系統)通過彈框進一步讓用戶確認是否授權當前可執行文件來達到阻止惡意程序的目的。
從圖中看可以理解為:
想要獲得管理員權限:
UAC 的實現:
ACL: windows 中的所有資源都有ACL。給各個程序劃定了權限。
在開啟了UAC之后,如果用戶是標準用戶,Windows 會給用戶分配一個標準Access Token
如果用戶以管理員權限登錄,會生成兩份訪問令牌。一份是完整的管理員訪問令牌。(full access token),一份是標準用戶令牌。
一般情況下會以標準用戶權限啟動Explore.exe 進程。如果用戶同意,則賦予完整管理員權限訪問令牌進行操作。
總結如下:
當一個管理員登錄到計算機時,Windows Vista的處理方式卻與先前版本的Windows有所不同。雖然系統創建了一個新的登錄會話,但卻為該登錄會話創建了兩個不同的訪問令牌,而不是先前版本中的一個。第一個訪問令牌提供了管理員所有的許可和權限,而第二個就是所謂的“受限訪問令牌”,有時候也叫做“過濾訪問令牌(filtered token)",該令牌提供了少得多的許可和權限。實際上,受限訪問令牌所提供的訪問權限和標準用戶的令牌沒什么區別。然后系統將使用該受限訪間令牌創建 shell應用程序。這也就意味著即使用戶是以管理員身份登錄的,其默認的運行程序許可和權限仍為標準用戶。
可以使用whoami /priv 查看當前權限。
(左邊是普通權限,右邊是管理員權限)
UAC架構
如圖:
UAC虛擬化
UAC虛擬化也被稱為重定向。在權限判定的過程中,如果用戶的權限沒有達到程序所需的權限,UAC就會重定向到該文件夾。
例如: 如果程序試圖寫入到C:\Program Files\Contoso\Settings.ini 目錄下,但用戶沒有寫入權限。這個寫操作就會被重定向為:
C:\Users\Username\AppData\Local\VirtualStore\Program Files\contoso\settings.ini
虛擬化被分為文件虛擬化和注冊表虛擬化。
需要注意的是,在以下條件下,虛擬化不可用
64 位程序
非交互式程序
模擬令牌的進程 (Processes that impersonate)
內核模式調用方
帶有 requestedExecutionLevel(請求執行等級)的可執行程序
文件虛擬化
注冊表虛擬化:
UAC原理初探
使用procexp 查看進程,然后用管理員權限運行一個文件會彈出UAC 提示框,會發現多出一個進程consent.exe
進程中有一個dll appinfo。 而UAC的主要實現文件就在于appinfo.dll 這個文件
對該appinfo.dll 逆向分析:
使用很多RPC Functions ,使得一個程序可以調用另一計算機的字程序。
本地過程調用(LPC) 則是在本機進程間進行通訊。
AppInfo是一個本地RPC服務,其接口ID為201ef99a-7fa0-444c-9399-19ba84f12a1a。該RPC服務中的RAiLaunchAdminProcess函數用于在權限不一致需要向上提權時進行UAC路由分發,具有以高權限啟動進程的功能。當被啟動的程序屬于系統目錄中的白名單進程時可避免彈窗以管理員權限啟動。
查看其導出函數,會發現其使用的RPCRT4 Library較多。
這個dll就是通過注冊LPC接口進行調用
RPC Functions (Remote Procedure Call),使得一個程序可以調用另一個計算機子程序。
LPC本地過程調用, 則是在本機進行進程間進行通訊。
這個dll就是通過注冊LPC接口進行調用。
本地過程調用通常也被稱為輕量過程調用或者本地進程間通訊,通過這一方式,同一計算機上的進程可以進行通信。在多任務操作系統中,它能使同時允許的任務能相互回話,這些任務共享內存空間。
以RpcServerRegisterIfEx 為例。這個函數是用來注冊LPC/RPC的接口的。 到底注冊了什么接口呢?
先了解一下Windows如何創建進程。(下一節探討)
而我們知道AicLaunchAdminProcess 是可以創建管理員進程的。
UAC進程啟動原理:
我們知道,我們在cmd中或者shell中雙擊一個程序,運行程序,都是通過ShellExecute來執行的。那么如果執行一個需要管理權限的程序,就會彈出UAC安全框
,其中的過程是怎樣的呢
可以跟蹤執行堆棧:
# ChildEBP RetAddr Args to Child 00 001febb8 76ff6a04 752069dc 00000002 001fec0c ntdll!KiFastSystemCallRet 01 001febbc 752069dc 00000002 001fec0c 00000001 ntdll!NtWaitForMultipleObjects+0xc 02 001fec58 7641bc8e 001fec0c 001fec80 00000000 KERNELBASE!WaitForMultipleObjectsEx+0x100 03 001feca0 76ef62f9 00000002 7ffdd000 00000000 kernel32!WaitForMultipleObjectsExImplementation+0xe0 04 001fecf4 76ef37f7 00000024 001fedd0 ffffffff user32!RealMsgWaitForMultipleObjectsEx+0x13c 05 001fed10 7574eacc 00000001 001fedd0 00000000 user32!MsgWaitForMultipleObjects+0x1f 06 001fed64 7574ea55 001fedac ffffffff 00000000 shell32!AicpMsgWaitForCompletion+0x3c 07 001fed84 7574e927 001fedac 00000001 ffffffff shell32!AicpAsyncFinishCall+0x33 08 001ff0b4 7574e60f 004285a4 003d4c38 00000000 shell32!AicLaunchAdminProcess+0x39c 09 001ff1a8 75692bda 000701be 00000000 004285a4 shell32!_SHCreateProcess+0x331 *****************√ 0a 001ff1fc 756853c5 00000001 00426818 00000001 shell32!CExecuteApplication::_CreateProcess+0xfc 0b 001ff20c 75685379 00426818 00426820 00000001 shell32!CExecuteApplication::_TryCreateProcess+0x2e 0c 001ff21c 756847d1 00426820 00403170 001ff24c shell32!CExecuteApplication::_DoApplication+0x48 0d 001ff22c 7569f6b1 00426820 00403170 00426818 shell32!CExecuteApplication::Execute+0x33 0e 001ff24c 75684a1c 00426820 00401bfc 00403178 shell32!CExecuteAssociation::_DoCommand+0x88 0f 001ff270 7569f733 00000000 003e11a8 762d3472 shell32!CExecuteAssociation::_TryApplication+0x41 10 001ff290 75684b63 00403178 003d6248 003d6248 shell32!CExecuteAssociation::Execute+0x5f 11 001ff2bc 75692260 003e11a8 003d6248 001ff398 shell32!CShellExecute::_ExecuteAssoc+0x8c 12 001ff2d8 75686772 00000000 00008140 003d6248 shell32!CShellExecute::_DoExecute+0x89 13 001ff2ec 75691efa 001ff398 00008140 001ff398 shell32!CShellExecute::ExecuteNormal+0x8b 14 001ff300 75691e88 001ff398 0000003c 4a135260 shell32!ShellExecuteNormal+0x33 15 001ff318 4a12580f 001ff398 2aeaa614 003cc870 shell32!ShellExecuteExW+0x62通過跟蹤,會發現最終請求到了shell32!_SHCreateProcess。查看這個請求的反匯編代碼。 在這里可以看到調用了CreateProcessAsUserW API。可以簡單理解為,修改被UAC進程啟動程序的父進程為原程序。
當用戶允許一次UAC提權時,AIS服務(AppInfo Service)調用的CreateProcessAsUser() 函數創建進程并且賦予恰當的管理員權限,在理論上說AIS服務(所在的進程)是提權后辣個進程的父進程。
然而,當我們用一些進程查看管理工具 進行查看時,會發現已經被提權的進程,它的父進程是創建它的進程,而不是AIS服務(所在的進程)。
這是因為AIS利用了CreateProcessAsUser() API中的一個新的功能,這里的新功能就是將要被提權的進程的父進程設置成創建該進程的進程,如果我們利用一下該API,就可以把一個進程的父進程設置為任意進程。
關于CreateProcessAsUser
如果是CreateProcessAsUser 的dwCreationFlags 的參數被設置為EXTENDED_STARTUPINFO_PRESENT, 這就是有擴展啟動信息的結構體,
這里的IpStartupInfo參數需要填好STARTUPEX 結構
這個結構由STARTUOINFO結構和PROC_THREAD_ATTRIBUTE_LIST 指針構成
也就是說,調用CreateProcessAsUser 且設置dwCreationFlags參數的值為EXTENDED_STARTUPINFO_PRESENT,此時再將結構體中的PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 值設置目標父進程的屬性。 具體例子在文章最后。
繼續看反編譯代碼
如果發現” 請求的操作需要提升“ 即用戶確定,則使用AicLaunchAdminProcess提權啟動。
由此我們知道,只有在explorer 建立管理員進程時,AicLaunchAdminProcess 才會被調用,否則建立普通進程調用調用的是KERNELBASE!CreateProcessInternalW
在以管理員身份運行程序時CInvokeCreateProcessVerb::CallCreateProcess會去調用AicLaunchAdminProcess,而AicLaunchAdminProcess本身并不拉起進程,而是做了rpc通信
整個過程為:
AicLaunchAdminProcess 會構造RPC請求,并等待RPC返回結果:
啟動過程就是RPC遠程調用的的過程。
使用如下兩種方式等待返回:
SVCHOST 服務
RPC 遠程過程調用的響應進程為C:\Windows\system32\svchost.exe -k netsvcs ,首先啟動一個UAC對話框進程。
如上圖 啟動consent.exe進程
該進程啟動之后,將會在桌面彈框:
consent.exe 堆棧如下:
等待consent.exe 驗證完成之后,啟動真實進程。如下:
再次總結:
通過以上描述,再進行一次梳理。
我們會發現盡管在explorer中使用管理員身份啟動的進程是explorer,但如果在explorer進程中對KERNELBASE!CreateProcessW下斷,使用管理員權限運行,也并不會斷下。而正常的啟動程序可以斷下來。
通過調試分析:
CInvokeCreateProcessVerb::CallCreateProcess會去調用AicLaunchAdminProcess,而AicLaunchAdminProcess本身并不拉起進程,而是做了rpc通信,真正拉起權限提升進程的程序并非是explorer
根據上圖中的uuid信息,可以在rpcview找到對應的接口,是一個svchost啟的服務。
從啟動命令可以發現為 appinfo, 并且根據rpcview中顯示的procedure 地址,可以找到對應的dll,也就是appinfo.dll
然后根據接口地址找到對應函數RAiLaunchAdminProcess,可以看到最終調用函數AiLaunchProcess,而該函數是對CreateProcessAsUserW的封裝。而權限提升的進程最終是由appinfo服務進程拉起來的。
UAC彈框流程
找到UAC彈框的函數:
以管理員權限打開一個程序,彈出uac窗口之后,斷下來,切換到appinfo服務所在的進程,打印所有線程棧。
可以發現, UAC彈框流程為:
RAiLaunchAdminProcess -> AiCheckLUA -> AiLaunchConsentUI
查看函數AiLaunchConsentUI, 發現構造命令行之后會調用AiLaunchProcess 來啟動consent.exe 它是真正繪制窗口的程序。
通過對consent.exe進行逆向
在consent繪制的窗口上,可以看到要進行權限提升的程序的路徑,命令行等相關信息。consent是如何獲取這些信息 的?
答案是consent的命令行中傳入了父進程的pid(appinfo服務的進程pid),包含一個結構體長度及一個指向結構體的指針,隨后consent調用NtReadVirtualMemory從父進程內存中讀取結構體內容。這個結構體當中就包含了需要特權提升的進程信息。
還有一個問題是特權提升的進程最終是由appinfo服務進程拉起的,但是UAC窗口則是consent繪制的,consent如何將用戶的操作反饋給appinfo服務進程。
同意是通過寫appinfo進程的內存實現。
通過逆向找到決定是否彈框的關鍵函數:
關鍵代碼:
可以知道consent是否彈框主要由父進程傳入結構體確定。
繼續回看appinfo
可以看到有一些白名單校驗,屬性校驗等。滿足這些條件之后,便不會彈框。
令牌權限提升過程
權限提升的過程位于consent中,consent從appinfo服務進程中獲取未授權提升的令牌后,調用NtQueryInformationToken 獲取一個權限提升的令牌(undocument的用法),隨后將這個token寫回到appinfo服務進程中,然后再利用這個提升后的令牌創建進程。
通過NtQueryInformationToken獲取權限提升令牌
如圖 將令牌寫回到appinfo中
觸發UAC方式:
* 配置Windows Update * 增加或刪除用戶賬戶 * 改變用戶的賬戶類型 * 改變UAC設置 * 安裝ActiveX * 安裝或移除程序 * 安裝設備驅動程序 * 設置家長控制 * 將文件移動或復制到Program Files或Windows目錄 * 查看其他用戶文件夾觸發流程:
在觸發UAC時,系統會創建一個consent.exe進程。該進程用以確定是否創建管理員進程。(根據白名單和用戶選擇判斷)然后createProcess 請求進程
將要請求的進程cmdline 和進程路徑 通過 LPC接口傳遞給appinfo 的 RAiLuanchAdminProcess函數
該函數首先驗證路徑是否在白名單中,并將結果傳遞給consent.exe 進程。
consent.exe進程驗證被請求的進程簽名和發起者權限是否符合要求,然后彈出UAC彈框
UAC彈框會創建新的安全桌面,屏蔽之前的界面。
這個UAC進程是SYSTEM權限進程,且其他普通進程無法與之進行通信交互。
用戶確認之后,調用CreateProcessAsUser 函數以管理員權限啟動請求的進程。
例子:
利用 CreateProcessAsUser 偽造父進程。
//一般情況下,創建子進程的進程 就是子進程的父進程。而這部分代碼功能: 去指定任何一個有相應權限的進程為新創建進程的父進程#include <iostream> #include <Windows.h> #include <Tlhelp32.h> //因為Tlhelp32.h 中一些宏定義是在windows.h中定義的,所以Tlhelp32.h 要定義在windows.h的后面 #pragma comment(lib, "Advapi32.lib")int main() { /LPVOID pAlloc1;LPVOID pAlloc2;HANDLE hfile;PIMAGE_NT_HEADERS pPeHeader;PIMAGE_SECTION_HEADER pSectionHeader;int lastError, ReadInfo = 0;DWORD BytesRead = 0;CONTEXT Context = { 0 };Context.ContextFlags = CONTEXT_ALL;PROCESSENTRY32 pe;// explorer.exe的進程IDDWORD pid = 0;HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);pe.dwSize = sizeof(PROCESSENTRY32);if (!Process32First(hSnapshot, &pe))return 0;do{pe.dwSize = sizeof(PROCESSENTRY32);if (Process32Next(hSnapshot, &pe) == FALSE)break;if (wcscmp(pe.szExeFile, L"cmd.exe") == 0){pid = pe.th32ProcessID;break;}} while (1);CloseHandle(hSnapshot);/* 以全部權限打開explorer.exe 進程 */HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);/* 創建啟動信息結構體 */STARTUPINFOEXA si;/* 初始化結構體 */ZeroMemory(&si, sizeof(si));/* 設置結構體成員 */si.StartupInfo.cb = sizeof(si);SIZE_T lpsize = 0;/* 用微軟規定的特定的函數初始化結構體 */InitializeProcThreadAttributeList(NULL, 1, 0, &lpsize);//首先要獲取到需要初始化的大小char * temp = new char[lpsize];/* 轉換指針到正確類型 */LPPROC_THREAD_ATTRIBUTE_LIST AttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)temp;/* 真正為結構體初始化屬性參數 */InitializeProcThreadAttributeList(AttributeList, 1, 0, &lpsize);//設置AttributeList結構體屬性個數以及初始化它的大小/* 用已構造的屬性結構體更新屬性表 */if (!UpdateProcThreadAttribute(AttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &handle, sizeof(HANDLE), NULL, NULL)){//更新AttrubuteList 屬性,添加PROC_THREAD_ATTRIBUTE_PARENT_PROCESS屬性printf("UpdateProcThreadAttribute failed ! (%d).\n", GetLastError());}/* 移交指針,這里已更換了父進程的屬性表是 explorer.exe */si.lpAttributeList = AttributeList;PROCESS_INFORMATION pi;ZeroMemory(&pi, sizeof(pi));//當調用下面的api 且createFlags的參數是EXTENDED_STARTUPINFO_PRESENT時,lpStartupInfo就需要有擴展的信息,也就是這條屬性:PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;if (CreateProcessAsUserA(NULL, 0, "C:\\Users\\Administrator\\Desktop\\Play.exe", 0, 0, 0, EXTENDED_STARTUPINFO_PRESENT, 0, 0, (LPSTARTUPINFOA)&si, &pi)){printf("CreateProcessAsUserA success ! \n");}else{printf("CreateProcessAsUserA failed ! (%d). \n ", GetLastError());}/* 處理后事 */DeleteProcThreadAttributeList(AttributeList);delete temp;printf("exit");getchar();return 0; }歡迎各位大佬一起來免費知識星球學習: 一起探究安全原理,探索更多攻擊方法。
https://t.zsxq.com/2rjA6yn
REFERENCE
https://citrusice.github.io/posts/uac-reversing/
https://www.i4k.xyz/article/xiangbaohui/103704153
https://www.cnblogs.com/chesky/p/uac_bypass.html
總結
以上是生活随笔為你收集整理的【UAC】Windows UAC 原理浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Redux/Mobx] 举例说明怎么在
- 下一篇: mysql全备份+增量备份笔记总结