作者:Loong716@Amulab
0x00 前言
在之前的文章中,分别向大家介紹了Windows通路控制模型中的SID和Access Token,本篇文章中将為大家介紹最後一個概念——特權
Windows作業系統中許多操作都需要有對應的特權,特權也是一種非常隐蔽的留後門的方式。在AD域中,一些特權在Default Domain Controller Policy組政策中被授予給一些特殊的組,這些組的成員雖然不是域管,但如果被攻ji者控制同樣能給AD域帶來巨大的風險
是以對防禦者來講,排查使用者的特權配置也是重中之重,本文将對一些比較敏感的特權進行介紹,便于防禦者更好的了解特權的概念以及進行排查
0x01 令牌中的Privilege
特權是一個使用者或組在本地計算機執行各種系統相關操作(關閉系統、裝載裝置驅動程式、改變系統時間)的權限,特權與通路權限的差別如下:
- 特權控制賬戶對系統資源和系統相關任務的通路,而通路權限控制對安全對象(可以具有安全描述符的對象)的通路
- 系統管理者為使用者或組指派特權,而系統根據對象的DACL中的ACE授予或拒絕對安全對象的通路,有時擁有特權可以忽略ACL的檢查
在之前介紹Access Token的文章中我們已經了解過了token的基本結構,其中有一部分表示了該使用者及該使用者所屬組所擁有的特權,如下圖所示:

通常我們會使用
whoami /priv
指令檢視目前使用者所擁有的特權,預設情況下大部分特權是禁用狀态,在使用時需要啟用
0x02 mimikatz的privilege子產品
mimikatz中的privilege子產品主要有以下功能,下圖中第一個紅框中的部分是為目前程序啟用一些指定的特權,第二個紅框中的
id
和
name
分别支援指定特權的id和名稱,并為目前程序啟用id和名稱對應的特權
通常我們比較通用的啟用程序特權的方法是這樣的,代碼如下:
BOOL GetDebugPrivilege()
{
BOOL status = FALSE;
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tokenPrivs;
tokenPrivs.PrivilegeCount = 1;
if (LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &tokenPrivs.Privileges[0].Luid))
{
tokenPrivs.Privileges[0].Attributes = TRUE ? SE_PRIVILEGE_ENABLED : 0;
if (AdjustTokenPrivileges(hToken, FALSE, &tokenPrivs, sizeof(tokenPrivs), NULL, NULL))
{
status = TRUE;
}
}
else wprintf(L"[!] LookupPrivilegeValueW error: %u when get debug privilege.\n", GetLastError());
CloseHandle(hToken);
}
else wprintf(L"[!] OpenProcessToken error: %u when get debug privilege.\n", GetLastError());
return status;
}
而mimikatz是通過調用一個未文檔化的API
RtlAdjustPrivilege()
,該API的功能是對目前程序或線程啟用/禁用指定的特權,共有四個參數:
- ULONG Privilege:需要操作的特權的ID
- BOOLEAN Enable:啟用或禁用的标志,1為啟用,0為禁用
- BOOLEAN CurrentThread:指定是否為目前線程,1則設定線程令牌,0則設定程序令牌
- PBOOLEAN Enabled:該特權修改之前是禁用的還是啟用的
NTSTATUS RtlAdjustPrivilege
(
ULONG Privilege, // [In]
BOOLEAN Enable, // [In]
BOOLEAN CurrentThread, // [In]
PBOOLEAN Enabled // [Out]
)
如果參數指定的是特權的名稱,則會先調用
LookupPrivilegeValue()
拿到特權名稱對應的特權ID,然後再調用
RtlAdjustPrivilege()
來啟用特權
前面提到的是将禁用的特權啟用,而如果想給一個賬戶賦予特權,則可以通過本地政策/組政策來設定,也可以通過
LsaAddAccountRights()
這個API,這裡不再贅述
0x03 危險的特權
這裡主要介紹11個危險的特權,在檢查域内安全時要格外注意
1. SeDebugPrivilege
通常情況下,使用者隻對屬于自己的程序有調試的權限,但如果該使用者Token中被賦予
SeDebugPrivilege
并啟用時,該使用者就擁有了調試其他使用者程序的權限,此時就可以對一些高權限程序執行操作以擷取對應的權限,以程序注入為例:
2. SeBackupPrivilege
該特權代表需要執行備份操作的權限,授予目前使用者對所有檔案的讀取權限,不受檔案原本的ACL限制,主要有以下利用思路:
- 備份SAM資料庫
- 備份磁盤上高權限使用者的敏感檔案
- 域内在域控上備份ntds.dit
下圖以導出系統資料庫中的SAM和SYSTEM為例
觀察上圖可能有師傅會問:為什麼前面顯示
SeBackupPrivilege
是Disable狀态,卻能成功執行reg save呢?一開始我猜測可能是reg.exe在執行操作前預設會啟用一些特權,随後通過對reg.exe的逆向也印證了這點:
在域環境中,Backup Operators和Server Operators組成員允許在域控進行本地登入,并在域控上擁有
SeBackupPrivilege
特權,是以也可以對ntds.dit進行備份操作,再備份系統資料庫中的SYSTEM和SECURITY,進而解密ntds.dit
需要注意的是在調用
CreateFile()
時,需要指定
FILE_FLAG_BACKUP_SEMANTICS
标志來表示正在為備份或恢複操作打開或建立檔案,進而覆寫檔案的ACL檢查
HANDLE hFile = CreateFileW(
L"C:\\Windows\\System32\\1.txt",
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
3. SeRestorePrivilege
該特權是執行還原操作所需的權限,擁有此特權的使用者對所有檔案擁有寫權限,不受檔案原本的ACL限制,主要利用思路如下:
- 修改系統資料庫,實作修改服務、修改啟動項等操作
- 寫檔案進行DLL劫持
域環境中,Backup Operators和Server Operators組成員同樣在域控上也有
SeRestorePrivilege
,是以也可以利用上述操作在域控上完成提權和維權等操作
需要注意的仍是調用API時,需要指定對應的标志,如
CreateFile()
需要指定
FILE_FLAG_BACKUP_SEMANTICS
,
RegCreateKeyEx()
REG_OPTION_BACKUP_RESTORE
4. SeTakeOwnershipPrivilege
該特權用來修改目标對象的所有權,也就是說擁有該特權的使用者可以修改任意對象的所有者(Owner),而所有者對該對象是有WriteDACL的權限的,可以任意修改對象的ACL
是以如果擁有了
SeTakeOwnershipPrivilege
,就相當于對任意對象有讀寫的權限,利用方式和
SeRestorePrivilege
、
SeBackupPrivilege
基本相同
GetTakeOwnershipPriv();
...
status = SetNamedSecurityInfo(
L"C:\\Windows\\System32\\localspl.dll",
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION,
user->User.Sid,
NULL,
NULL,
NULL);
如下圖所示,可以将對象的Owner從TrustedInstaller修改為目前使用者:
5. SeImpersonatePrivilege
當
SeImpersonatePrivilege
特權配置設定給使用者時,表示允許該使用者運作的程式模拟用戶端,預設Service賬戶(如MSSQL、IIS的服務賬戶)和管理者賬戶會擁有該權限
該權限也是一些potato提權的重要條件,可以通過printbug+
ImpersonateNamedPipeClient()
等等許多方式擷取到高權限令牌,進而執行模拟,此處以pipepotato為例:
6. SeAssignPrimaryTokenPrivilege
該特權表示可以為程序配置設定主令牌,經常與
SeImpersonatePrivilege
特權配合使用在potato的提權中。擁有該特權時,我們可以使用非受限的令牌調用
CreateProcessAsUser()
;或者先建立挂起的程序,再通過
NtSetInformationProcess()
來替換程序的token
順便提一嘴,之前文章中提到的mimikatz的token::run子產品在使用時可能會出現0x00000522錯誤,如下圖所示
這是因為在調用
CreateProcessAsUser()
時,如果傳入的是非受限令牌,那麼則需要
SeAssignPrimaryTokenPrivilege
特權,有關受限令牌的概念可閱讀微軟文檔:https://docs.microsoft.com/en-us/windows/win32/secauthz/restricted-tokens
是以該功能應該是用來從SYSTEM權限竊取其他使用者的Access Token(因為預設SYSTEM才有
SeAssignPrimaryTokenPrivilege
),如果想要非SYSTEM使用者調用的話可以考慮改為用
CreateProcessWithToken()
建立程序
7. SeLoadDriverPrivilege
該權限用來加載或解除安裝裝置的驅動,在windows中使用者可以通過
NTLoadDriver()
進行驅動的加載,其DriverServiceName參數需要傳入驅動配置的系統資料庫項
NTSTATUS NTLoadDriver(
_In_ PUNICODE_STRING DriverServiceName // \Registry\Machine\System\CurrentControlSet\Services\DriverName
);
其中DriverName表示啟動名稱,該鍵下至少應有兩個值:
- ImagePath:REG_EXPAND_SZ類型,“??\C:\path\to\driver.sys” 格式
- Type:REG_WORD類型,其值需要被設定為1,表示KENERL_DRIVER
如果是非管理者權限,預設無法操作HKLM系統資料庫項,則可以在HKEY_CURRENT_USER (HKCU) 下建立系統資料庫項并設定驅動程式配置設定,再調用
NTLoadDriver()
指定之前建立的系統資料庫項來注冊驅動,代碼可參考:https://github.com/TarlogicSecurity/EoPLoadDriver/
此時可以利用一些有漏洞的驅動程式來實作LPE等操作,以Capcom.sys為例:
除此之外,在AD域中
SeLoadDriverPrivilege
權限在域控上預設授予Print Operators組,使得該組使用者可以遠端在域控加載列印機驅動程式,前一段時間的Printnightmare便是繞過了該權限的檢查
8. SeCreateTokenPrivilege
該特權表示:允許擁有此特權的程序可以通過
ZwCreateToken()
建立Access Token
NTSATUS ZwCreateToken(
OUT PHANDLE TokenHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN TOKEN_TYPE TokenType,
IN PLUID AuthenticationId,
IN PLARGE_INTEGER ExpirationTime,
IN PTOKEN_USER TokenUser,
IN PTOKEN_GROUPS TokenGroups,
IN PTOKEN_PRIVILEGES TokenPrivileges,
IN PTOKEN_OWNER TokenOwner,
IN PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
IN PTOKEN_DEFAULT_DACL TokenDefaultDacl,
IN PTOKEN_SOURCE TokenSource
);
那麼我們肯定會想:能不能直接利用該API建立一個SYSTEM的token,然後起程序?很遺憾,該權限不允許使用者使用他們剛建立的令牌
但我們可以利用模拟,建立一個目前使用者的、包含特權組SID的token,因為隻要令牌是針對同一個使用者的,并且完整性級别小于或等于目前程序完整性級别(完整性級别可以通過構造令牌時來設定),就可以不需要
SeImpersonatePrivilege
特權,對線程設定模拟令牌
以建立Group List中包含administrators組SID的token為例,在建立token前修改了組SID、特權清單,最初成功利用模拟令牌建立線程,在system32下寫入檔案:
需要注意的是在Win10 >= 1809和Windows Server 2019,以及安裝了KB4507459的Win10和2016上,我們不能使用生成的模拟令牌,會爆“1346:未提供所需的模拟級别,或提供的模拟級别無效”錯誤
幸運的是已經有大牛發現了繞過的方法,就是把Token的AuthenticationID從
SYSTEM_LUID
(0x3e7)修改為
ANONYMOUS_LOGON_LUID
(0x3e6),最終成功使用模拟令牌向system32目錄寫入了檔案:
9. SeTcbPrivilege
該特權标志着其擁有者是作業系統的一部分,擁有該特權的程序可利用
LsaLogonUser()
執行建立登入令牌等操作,是以可以充當任意使用者
NTSTATUS LsaLogonUser(
HANDLE LsaHandle,
PLSA_STRING OriginName,
SECURITY_LOGON_TYPE LogonType,
ULONG AuthenticationPackage,
PVOID AuthenticationInformation,
ULONG AuthenticationInformationLength,
PTOKEN_GROUPS LocalGroups,
PTOKEN_SOURCE SourceContext,
PVOID *ProfileBuffer,
PULONG ProfileBufferLength,
PLUID LogonId,
PHANDLE Token,
PQUOTA_LIMITS Quotas,
PNTSTATUS SubStatus
);
根據微軟官方文檔,當以下一項獲多項為真時,
LsaLogonUser()
調用者需要
SeTcbPrivilege
特權:
- 使用了 Subauthentication 包
- 使用 KERB_S4U_LOGON,調用者請求模拟令牌
-
參數不為NULLLocalGroups
我們主要關注第二點和第三點,從文檔的描述來看,如果使用KERB_S4U_LOGON來登入(也可以使用MSV1_0_S4U_LOGON,但文檔中未展現),我們就可以拿到一張模拟令牌,并且可以在
LocalGroups
參數給該令牌添加附加組:
WCHAR systemSID[] = L"S-1-5-18";
ConvertStringSidToSid(systemSID, &pExtraSid);
pGroups->Groups[pGroups->GroupCount].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY;
pGroups->Groups[pGroups->GroupCount].Sid = pExtraSid;
pGroups->GroupCount++;
此時我們就可以拿到一張擁有SYSTEM的SID的令牌,如何在沒有
SeImpersonatePrivilege
特權的情況下使用模拟令牌在
SeCreateTokenPrivilege
的利用中已經提到過了
如下圖所示,成功在system32下寫入檔案:
當然,如果在域内,也可以嘗試KERB_S4U_LOGON來擷取域内使用者的模拟令牌
10. SeTrustedCredmanAccessPrivilege
該特權用來通路憑據管理器,備份憑據管理器中的憑據需要使用
CredBackupCredentials()
這一API,而調用該API需要擁有
SeTrustedCredmanAccessPrivilege
特權,該特權預設授予winlogon.exe和lsass.exe這兩個程序
BOOL WINAPI CredBackupCredentials( HANDLE Token, LPCWSTR Path, PVOID Password, DWORD PasswordSize, DWORD Flags);
為了測試我在憑據管理器中手動新增了一條憑據,用于通路192.168.47.20,使用者名和密碼為admin/adminpass
利用方式即竊取winlogon.exe的token,并調用
CredBackupCredentials()
對憑據管理器中的憑據進行備份(指定加密密碼為NULL),最終再調用
CryptUnprotectData()
對備份的檔案進行解密。此處代碼參考:https://github.com/BL0odz/POSTS/blob/main/DumpCred\_TrustedTokenPriv/main.cpp
11. SeEnableDelegationPrivilege
在域内配置無限制委派和限制委派時(這裡特指傳統的限制委派,不包括基于資源的限制委派),都是修改的LDAP中的
userAccountControl
屬性來配置(當然限制委派還要修改
msDS-AllowedToDelegateTo
來配置委派可以通路的服務),而想要配置無限制委派或限制委派,不僅需要對屬性有寫權限,還需要在域控有
SeEnableDelegationPrivilege
特權
雖然該利用對攻ji者來說較為苛刻,但如果發現域内組政策給普通賬戶配置了
SeEnableDelegationPrivilege
特權,就需要檢查是否是正常的業務需求
0x04 檢測與緩解
檢測思路:
- 檢視域内Server Operators、Backup Operators、Print Operators等特權組内是否有不應出現的使用者
- 檢視域内組政策配置檔案,是否有将特權授予不常見的SID
- 檢測“4672: 配置設定給新登入的特殊權限”日志
緩解思路:
- 非業務必需情況下不為普通賬戶賦予特權
- 不影響業務的情況下,可以取消部分管理者賬戶的
等特權SeDebugPrivilege
0x05 參考
https://docs.microsoft.com/
https://github.com/gentilkiwi/mimikatz
https://bbs.pediy.com/thread-76552.htm
https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-Windows%E4%B9%9D%E7%A7%8D%E6%9D%83%E9%99%90%E7%9A%84%E5%88%A9%E7%94%A8
https://github.com/hatRiot/token-priv/blob/master/abusing\_token\_eop\_1.0.txt
https://hackinparis.com/data/slides/2019/talks/HIP2019-Andrea\_Pierini-Whoami\_Priv\_Show\_Me\_Your\_Privileges\_And\_I\_Will\_Lead\_You\_To\_System.pdf