作者:御守實驗室
原文鏈接:https://mp.weixin.qq.com/s/eMXaerqdkrq4maHuzII9BA
相關閱讀:《從 mimikatz 學習 Windows 安全之訪問控制模型(二)》
0x00 前言
Mimikatz是法國安全研究員Benjamin Delpy開發的一款安全工具。滲透測試人員對mimikatz印象最深的肯定就是抓取Windows憑證,但作者對它的描述是“a tool I've made to learn C and make somes experiments with Windows security.”,其實它的功能不僅僅是抓取憑證,還包含了很多Windows安全相關的技術和知識
這里借用@daiker師傅的思維導圖,mimikatz的模塊大致可分為幾個部分:

因此文章也會大致分為windows 訪問控制模型,windows 憑據以及加解密,windows AD 安全,windows 進程以及服務,mimikatz 其他模塊五個小系列。之前自己一直想分析mimikatz的相關功能,主要是出于以下原因:
- mimikatz中有許多功能利用了Windows的一些機制和特性,以changentlm為例,其利用MS-SAMR協議修改用戶的密碼,我們再根據MS-SAMR或RPC進行知識延伸,肯定也有不少收獲
- mimikatz中涉及大量內存的操作,其中運用的內存Patch技術也被經常應用于一些安全機制的繞過(如繞過AMSI、Credential Guard等),于是自己想在分析過程中通過windbg學到一些調試的技巧
- mimikatz在實戰中被殺的很厲害,了解相應原理可以自己實現相應功能
- 學習/練習C語言 :D
mimikatz中與Windows訪問控制模型相關的有privilege、token、sid三個模塊,其分別對應特權、訪問令牌、安全標識符三個知識,本文主要分析token模塊,并簡要介紹Windows訪問控制模型
由于mimikatz代碼邏輯較為復雜,涉及大量回調,因此文中代碼都是經過簡化的。文章可能也會有一些技術上或者邏輯上的錯誤,還請師傅們指正
0x01 訪問控制模型簡介
Windows訪問控制模型有兩個基本組成部分:
- 訪問令牌(Access Token):包含有關登錄用戶的信息
- 安全描述符(Security Descriptor):包含用于保護安全對象的安全信息
1. 訪問令牌(Access Token)
訪問令牌(Access Token)被用來描述一個進程或線程的安全上下文,用戶每次登錄成功后,系統會為其創建訪問令牌,該用戶的所有進程也將擁有此訪問令牌的副本
當線程與安全對象進行交互或嘗試執行需要特權的系統任務時,系統使用訪問令牌來標識用戶。使用windbg查看進程的token,其包含信息如下圖所示:



2. 安全描述符(Security Descriptor)
安全描述符(Security Descriptor)包含與安全對象有關的安全信息,這些信息規定了哪些用戶/組可以對這個對象執行哪些操作,安全描述符主要由以下部分構成:
- 所有者的SID
- 組SID
- 自主訪問控制列表(DACL),規定哪些用戶/組可以對這個對象執行哪些操作
- 系統訪問控制列表(SACL),規定哪些用戶/組的哪些操作將被記錄到安全審計日志中
在windbg中查看一個安全對象的安全描述符,可以清晰的看到安全描述符的組成:

可以看到該安全描述符的DACL中有三條ACE,ACE的類型都是ACCESS_ALLOWED_ACE_TYPE,Mask是權限掩碼,用來指定對應的權限。以第一條ACE為例,其表示允許SID為S-1-5-32-544的對象能夠對該安全對象做0x001fffff對應的操作
3. 權限檢查的過程
當某個線程嘗試訪問一個安全對象時,系統根據安全對象的ACE對照線程的訪問令牌來判斷該線程是否能夠對該安全對象進行訪問。通常,系統使用請求訪問的線程的主訪問令牌。但是,如果線程正在模擬其他用戶,則系統會使用線程的模擬令牌
此時將在該安全對象的DACL中按順序檢查ACE,直到發生以下事件:
- 某一條拒絕類型的ACE顯式拒絕令牌中某個受信者的所有訪問權限
- 一條或多條允許類型的ACE允許令牌中列出的受信者的所有訪問權限
- 檢查完所有ACE但沒有一個權限顯式允許,那么系統會隱式拒絕該訪問
我們以微軟文檔中的圖片為例,描述一下整個過程:

- 線程A請求訪問安全對象,系統讀取ACE1,發現拒絕Andrew用戶的所有訪問權限,而線程A的訪問令牌是Andrew,因此拒絕訪問,并不再檢查ACE2、ACE3
- 線程A請求訪問,系統按順序讀取ACE,ACE1不適用,讀取到ACE2發現適用,再讀取到ACE3也適用,因此最終該用戶擁有對該安全對象的讀、寫、執行權限
0x02 Mimikatz的Token模塊
Mimikatz的token模塊共有5個功能:
- token::whoami:列出當前進程/線程的token信息
- token::list:列出當前系統中存在的token
- token::elevate:竊取其他用戶的token
- token::run:利用某用戶權限運行指定程序
- token::revert:恢復為原來的token
1. token::whoami
該功能用于列出當前進程/線程的token信息

只有一個可選參數/full,當指定該參數時會打印出當前token的組信息和特權信息:

該功能的原理大致如下:
- 通過
OpenProcess()獲取當前進程/線程的句柄 - 調用
GetTokenInformation()獲取token的各種信息并輸出
其核心為調用GetTokenInformation()來獲取token的各種信息,我們先來看這個API定義

其中第二個參數是一個TOKEN_INFORMATION_CLASS枚舉類型,我們可以通過指定它的值來獲取token指定的信息
例如獲取token的SessionID并輸出,可以使用以下代碼:

2. token::list
該功能是獲取當前系統中所有的token,注意使用前需要先獲取SeDebugPrivilege,否則列出的token不全

該功能原理大致如下:
- 調用
NtQuerySystemInformation()獲取系統進程信息(如進程PID等) - 循環遍歷所有進程的PID,使用
token::whoami功能中的方法對指定token信息進行輸出
NtQuerySystemInformation()用來檢索指定的系統信息:

其第一個參數是一個SYSTEM_INFORMATION_CLASS枚舉類型,我們同樣可以指定不同參數來獲取不同的系統信息

以獲取系統進程名和PID為例,代碼如下:

PS:按照該思路,理論上利用CreateToolhelp32Snapshot() + Process32First()遍歷進程PID也可以實現該功能
3. token::elevate
該模塊用于竊取指定用戶的token,共有7個可選參數,這些參數主要用來指定要竊取的token,如果不指定參數則默認竊取NT AUTHORITY\SYSTEM的token
- /id:指定目標token的TokenID
- /domainadmin:竊取域管的token
- /enterpriseadmin:竊取企業管理員的token
- /admin:竊取本地管理員的token
- /localservice:竊取Local Service權限的token
- /networkservice:竊取Network Service權限的token
- /system:竊取SYSTEM權限的token
假設我們現在在目標機器上發現的域管權限的token

我們可以指定目標TokenID,或者使用/domainadmin來竊取域管的token,執行成功后可以看到當前線程已經擁有域管的模擬令牌:

然后我們就可以在當前mimikatz上下文中使用域管身份執行操作了,如DCSync

該功能大致過程如下:
- 通過
OpenProcess()獲取當前進程/線程的句柄 - 調用
OpenProcessToken()打開與進程相關的token句柄 - 使用
DuplicateTokenEx()使用目標進程token創建一個新的模擬token - 調用
SetThreadToken()設置當前線程的token為上一步創建的新的模擬token
由于竊取token是Access Token利用的重點,該過程放在本文后面分析
4. token::run
該功能是使用指定的token來運行程序,也可以使用token::elevate中的幾個參數來指定運行程序的token,除此之外還有一個參數:
- /process:指定要運行的程序,默認值為whoami.exe

其原理前三步與token::elevate大致相同,區別在于使用DuplicateTokenEx()竊取token后,該功能使用CreateProcessAsUser()來使用新的primary token創建一個進程

創建進程后,利用匿名管道做進程間通信,將新創建進程的標準輸出寫入到匿名管道的write端,從管道read端讀取數據進行回顯(在webshell等非交互場景下很有用)

5. token::revert
該模塊用來清除線程的模擬令牌:

原理很簡單,直接使用SetThreadToken(NULL, NULL)即可將當前線程的token清除
0x03 令牌竊取
在滲透測試中,竊取token是administrator -> system的常見手法之一,還經常被用于降權等用戶切換操作
1. 原理
竊取token主要涉及以下幾個API:
1.OpenProcess

該函數打開指定PID的進程的句柄,需要注意的是第一個參數dwDesiredAccess,主要會用到的是以下三個權限
- PROCESS_ALL_ACCESS
- PROCESS_QUERY_INFORMATION (0x0400)
- PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
我在編寫竊取Token的代碼時,發現對部分進程(如smss.exe、csrss.exe等)調用OpenProcess會出現拒絕訪問的情況,查閱網上資料后發現這些進程存在保護,需要使用PROCESS_QUERY_LIMITED_INFORMATION權限打開句柄,詳情請參考這篇文章
2.OpenProcessToken

該函數打開與進程相關聯的令牌的句柄,其中第二個參數DesiredAccess同樣用來指定令牌的訪問權限,需要以下幾個:
- TOKEN_DUPLICATE:復制令牌需要的權限
- TOKEN_QUERY:查詢令牌需要的權限
如果要調用DuplicateTokenEx需要指定TOKEN_DUPLICATE,如果調用ImpersonatedLoggedOnUser則需要指定TOKEN_DUPLICATE和TOKEN_QUERY
3.DuplicateTokenEx

DuplicateTokenEx用來復制現有的令牌來生成一張新令牌,該函數可以選擇生成主令牌還是模擬令牌
-
hExistingToken:指定現有的令牌句柄,可以使用
OpenProcessToken獲得 -
dwDesiredAccess:用來指定令牌訪問權限,需要指定以下幾個來支持后面調用
CreateProcessWithToken: -
- TOKEN_DUPLICATE:需要復制訪問令牌
- TOKEN_QUERY:需要查詢訪問令牌
- TOKEN_ASSIGN_PRIMARY:將令牌附加到主進程的權限
- TOKEN_ADJUST_DEFAULT:需要更改訪問令牌的默認所有者、主要組或 DACL
-
TOKEN_ADJUST_SESSIONID:需要調整訪問令牌的會話 ID,需要 SE_TCB_NAME 權限
-
lpTokenAttributes:指向SECURITY_ATTRIBUTES結構的指針,該 結構指定新令牌的安全描述符并確定子進程是否可以繼承該令牌
-
ImpersonationLevel:指定令牌的模擬級別,當進行復制令牌時,主令牌被復制為模擬令牌是始終被允許的,而模擬令牌復制為主令牌則需要模擬級別 >= Impersonate
-
TokenType:指定新令牌的類型,是主令牌(Primary Token)還是模擬令牌(Impersonate Token)
-
phNewToken:返回令牌句柄的地址
復制完一張新令牌后,我們就可以利用這張新令牌來運行我們指定的進程了
4.CreateProcessWithTokenW

該函數創建一個新進程及其主線程,新進程在指定令牌的安全上下文中運行。我們直接指定前面復制出來的新令牌,使用該令牌創建我們指定的進程即可
2. 利用
根據mimikatz的token模塊的原理,簡單實現了一個demo,也有許多token相關的工具如incognito等
當獲取管理員權限后,我們可以列出系統中進程對應的token:

然后竊取指定進程的token來運行我們的程序,如直接運行上線馬

如果想要拿回程序輸出的話,則可以通過管道等進程間通信的方法來回顯輸出

如果拿到一臺機器有域管的進程,那么我們可以直接竊取域管進程的token來進行DCSync攻擊

0x04 參考鏈接
https://github.com/gentilkiwi/mimikatz/
https://www.ired.team/offensive-security/privilege-escalation/t1134-access-token-manipulation
https://www.slideshare.net/JustinBui5/understanding-windows-access-token-manipulation
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1669/
暫無評論