再探MS-SAMR協定
作者:Loong716@Amulab
0x00 前言
在前一篇《利用MS-SAMR協定修改使用者密碼》中介紹了利用MS-SAMR修改使用者密碼并還原的技巧。在本篇文章中,我們繼續介紹MS-SAMR協定的一些其它利用。
0x01 利用
1. 添加本地使用者
在滲tou測試過程中,我們經常會遇到在目标機器添加賬戶但被殺軟攔截掉的情況。現在較為通用的繞過方法是通過調用
NetUserAdd()
等API來添加使用者

我們同樣也可以利用MS-SAMR協定中的
SamrCreateUser2InDomain()
來添加使用者(其實調用MS-SAMR是
NetUserAdd()
等API的底層實作)
需要注意的有兩點,一點是Windows作業系統(域控除外)中的“域”分為内置域(Builtin Domain)和賬戶域(Account Domain)
- 内置域(Builtin Domain):包含在安裝作業系統時建立的預設本地組,例如管理者組和使用者組
- 賬戶域(Account Domain):包含使用者、組和本地組帳戶。管理者帳戶在此域中。在工作站或成員伺服器的帳戶域中定義的帳戶僅限于通路位于該帳戶所在實體計算機上的資源
是以我們需要在賬戶域中添加普通使用者,然後在内置域中找到Administrators組,再将該使用者添加到内置域中的Administrators中
第二個需要注意的是,利用
SamrCreateUser2InDomain()
添加的賬戶預設是禁用狀态,是以我們需要調用
SamrSetInformationUser()
在使用者的userAccountControl中清除禁用标志位:
// Clear the UF_ACCOUNTDISABLE to enable account
userAllInfo.UserAccountControl &= 0xFFFFFFFE;
userAllInfo.UserAccountControl |= USER_NORMAL_ACCOUNT;
userAllInfo.WhichFields |= USER_ALL_USERACCOUNTCONTROL;
RtlInitUnicodeString(&userAllInfo.NtOwfPassword, password.Buffer);
// Set password and userAccountControl
status = SamSetInformationUser(hUserHandle, UserAllInformation, &userAllInfo);
在實作時,如果直接調用MS-SAMR的話在設定使用者密碼時會非常複雜,涉及到加密算法并且可能需要SMB Session Key(用impacket很好實作,但impacket不支援目前使用者身份執行)
但我們可以調用samlib.dll的導出函數,在上一篇文章中提到過這些導出函數其實是封裝了協定的調用,實作會更簡單一些,代碼Demo:https://github.com/loong716/CPPPractice/tree/master/AddUserBypass_SAMR
2. 解決密碼過期限制
假設在滲tou測試過程中,我們收集到一台伺服器的使用者賬戶,但當想要通路目标SMB資源時,發現該賬戶密碼已過期。此處以psexec橫向為例,目标顯示STATUS_PASSWORD_MUST_CHANGE錯誤:
此時我們可以利用samba中的smbpasswd來修改該使用者的密碼
修改之後使用新密碼就可以正常通路目标的SMB資源了:
實際上smbpasswd調用的是MS-SAMR的
SamrUnicodeChangePasswordUser2()
,該方法不需要上下文句柄,并且支援SMB空會話(Null Session)調用
https://github.com/samba-team/samba/blob/e742661bd2507d39dfa47e40531dc1dca636cbbe/source3/libsmb/passchange.c#L192
另外impacket之前也更新了該方法的example,并且該腳本支援hash傳遞:
https://github.com/SecureAuthCorp/impacket/blob/master/examples/smbpasswd.py
3. 資訊收集/修改
MS-SAMR協定在資訊收集/修改方面能做的事情很多,如枚舉/修改對象的ACL、使用者&組資訊、枚舉密碼政策等。此處以枚舉本地管理者組賬戶為例
通常進行本地管理者組賬戶的枚舉會調用
NetLocalGroupGetMembers()
這一API,前面提到過這類API底層也是調用MS-SAMR協定,先來看一下正常調用的過程:
- SamrConnect:擷取Server對象的句柄
- SamrOpenDomain:打開目标内置域的句柄
- SamrLookupNamesInDomain:在内置域中搜尋Administrators的RID
- SamrOpenAlias:根據Administrators的RID打開别名句柄
- SamrGetMembersInAlias:枚舉别名對象中的成員SID
此時我們如果想要開發自動化的資訊收集工具(如SharpHound),那麼我們需要考慮工具的通用性,比如在第3步調用
SamrLookupNamesInDomain()
時,我們需要傳入"Administrators",但在某些系統中管理者組的名字可能有差異,如部分非英文作業系統中該組名為"Administradors",或者運維修改了本地管理者組名稱,這樣我們直接調用
NetLocalGroupGetMembers()
便不合适了
此時我們可以考慮優化這一操作,我們可以注意到本地管理者組在不同Windows系統上的RID始終為544
那麼我們可以這樣調用:
- SamrOpenAlias:打開RID為544對象的别名句柄
- SamrGetMembersInAlias:枚舉該别名對象中的成員SID
按此思路,我們可以将MS-SAMR的API利用到我們自己工具的武器化or優化上
4. 添加域内機器賬戶
調用
SamCreateUser2InDomain()
時指定AccountType為USER_WORKSTATION_TRUST_ACCOUNT可以在域内添加機器賬戶
// Create computer in domain
status = SamCreateUser2InDomain(hDomainHandle, &computerName, USER_WORKSTATION_TRUST_ACCOUNT, USER_ALL_ACCESS | DELETE | WRITE_DAC, &hUserHandle, &grantAccess, &relativeId);
impacket的addcomputer.py包含了該方法,因為LDAPS需要證書的不穩定是以添加了SAMR(SAMR是Windows GUI環境添加機器使用的協定)
這個地方感覺還是有一些誤區的,通過LDAP修改
unicodePwd
确實需要在加密的連接配接中操作,但LDAPS并不是必須的,像powermad.ps1在加密LDAP中添加機器賬戶同樣可以成功,并且非常穩定
我們實戰中大多數情況下添加機器賬戶都是在利用基于資源限制委派時,為了拿到一個有SPN的賬戶是以才選擇添加機器賬戶。但我實際測試中發現該方法并不會自動為機器賬戶添加SPN,而通過LDAP或其他RPC為機器賬戶添加SPN又感覺有些畫蛇添足,隻能先作為一種添加機器賬戶的實作方法,如果其他方法不成功時可以嘗試
0x02 參考
https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380