0x00 概述
本文簡要介紹了SMBv2中的一個有趣特性,可能用于橫向滲透或者紅方行動中。之前我曾專門花時間研究Windows上的符號鏈接(symbolic link)攻擊,當時我仔細研究過SMB服務器。從SMBv2開始,該協議就已支持符號鏈接(特別是NTFS 重解析點(Reparse Point)格式)。如果SMB服務器在共享目錄中遇到NTFS符號鏈接,則會提取REPARSE_DATA_BUFFER結構,然后遵循SMBv2協議中相應規范將該信息返回給客戶端。
客戶端操作系統負責解析REPARSE_DATA_BUFFER
數據,然后再從本地訪問。這意味著符號鏈接只能引用客戶端已經能夠訪問的文件。實際上,雖然默認情況下Windows沒有啟用符號鏈接本地解析功能,我還是找到了繞過客戶端策略的一種方法,能夠本地解析符號鏈接。目前微軟拒絕修復這個繞過問題,如果大家感興趣可以訪問此處了解官方回復。
0x01 問題描述
我發現有一點非常有趣,雖然IO_REPARSE_TAG_SYMLINK
會在客戶端上處理,但如果服務器遇到IO_REPARSE_TAG_MOUNT_POINT
重解析點,則會到服務器上去解析。因此,如果我們能在共享目錄中設置掛載點(mout point),就可以訪問服務器上的任意固定位置(即使該位置沒有直接共享出來)。這種場景在橫向滲透中非常有用,但問題在于,我們如何在無法本地訪問硬盤的情況下添加掛載點?
0x02 具體分析
首先我們嘗試一下通過UNC路徑創建掛載點,可以在CMD中使用MKLINK
命令,結果如下所示:
輸出信息表示系統不支持在遠程服務器上設置掛載點。這一點也能夠理解,因為在遠程驅動器上設置掛載點可能會導致不可預期后果。我們可以猜測一下,要么該協議不支持設置重解析點,要么做了些限制,只允許符號鏈接。如果想了解協議具體支持的功能,我們可以查看協議規范。設置重解析點需要向某個文件發送FSCTL_SET_REPARSE_POINT?IO控制代碼(control code),因此我們可以參考SMB2 IOCTL命令,查看其中是否存在與控制代碼有關的信息。
一番搜索后,我們可以看到協議的確支持FSCTL_SET_REPARSE_POINT
,并且協議規范中有如下描述(§3.3.5.15.13):
當服務器收到包含包含SMB2頭部的請求,并且Command值等于SMB2 IOCTL、
CtlCode
等于FSCTL_SET_REPARSE_POINT
時,那么消息處理過程如下:根據[MS-FSCC] section 2.3.65規范,如果
FSCTL_SET_REPARSE_POINT
中的ReparseTag
字段不等于IO_REPARSE_TAG_SYMLINK
,那么服務器應該驗證調用方的確有權限執行這個FSCTL
。如果調用方不具備所需的權限,那么服務器必須拒絕該調用,返回STATUS_ACCESS_DENIED
錯誤代碼。
根據上述文字,貌似服務器只需要顯示檢查IO_REPARSE_TAG_SYMLINK
即可,如果不匹配該標簽,則會執行其他檢查操作判斷請求是否允許,但并沒有提到服務器會設置另一個標簽來顯式禁止請求。也許系統內置的MKLINK
工具不能處理這種場景,換個工具試試?這里我們可以嘗試下CreateMountPoint
工具(來自于我的symboliclink-testing-tools項目),看能不能成功。
CreateMountPoint
工具并沒有顯示之前的錯誤(“只支持本地NTFS卷”),但返回了拒絕訪問錯誤。這與§3.3.5.15.13中的描述相符,如果隱式檢查失敗,應當返回拒絕訪問錯誤。當然協議規范中并沒有表明需要執行哪些檢查,我認為這時候應該派上反編譯工具,分析一下SMBv2驅動(srv2.sys
)的具體實現。
我使用IDA來查找IO_REPARSE_TAG_SYMLINK
對應的立即數(immediate value,這里為0xA000000C
),根據分析結果,貌似系統在查找其他標志時,會先會查找這個值。在Windows 10 1809系統的驅動中,我只在Smb2ValidateIoctl
找到一處匹配值,相關代碼大致如下:
NTSTATUS Smb2ValidateIoctl(SmbIoctlRequest* request) {
// ...
switch(request->IoControlCode) {
case FSCTL_SET_REPARSE_POINT:
REPARSE_DATA_BUFFER* reparse = (REPARSE_DATA_BUFFER*)request->Buffer;
// Validate length etc.
if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
!request->SomeOffset->SomeByteValue) {
return STATUS_ACCESS_DENIED;
}
// Complete FSCTL_SET_REPARSE_POINT request.
}
}
上述代碼首先從IOCTL請求中提取數據,如果標志不等于IO_REPARSE_TAG_SYMLINK
并且請求中某些字節值不等于0,那么就會返回STATUS_ACCESS_DENIED
錯誤。如果想跟蹤這個值的來源,有時候會比較棘手,但其實我只需要在IDA中將變量偏移值作為立即數來搜索,通常就能得出結論。這里對應的立即數為0x200
,我們可以只搜索MOV
指令。最終我在Smb2ExecuteSessionSetupReal
中找到了一條指令:MOV [RCX+0x200], AL
,這似乎就是我們想要的結果。系統會使用Smb2IsAdmin
函數的返回值來設置該變量,而該函數只會檢查調用方令牌中是否包含BUILTIN\Administrators
組。因此貌似只要我們是主機上的管理員,就可以在遠程共享上設置任意重解析點。我們需要驗證這一點:
以管理員賬戶測試時我們能創建掛載點,并且dir UNC路徑時,我們也能看到相應的Windows目錄。雖然我測試的是本地admin共享,但這適用于其他共享,并且也能訪問指向遠程服務器的掛載點。
0x03 總結
這種技巧是否有用武之地?這種方法需要管理員訪問權限,因此這并不是一種權限提升技術。此外,如果我們具備遠程管理員訪問權限,那么我們肯定會利用該權限執行其他操作。然而,如果目標主機禁用了admin共享,或者目標環境中有些監控機制,可以監控ADMIN$
或者C$
,但我們具備有些共享的寫入權限,那么橫向滲透中我們就可以使用這種方法完全控制其他驅動器。
我發現之前沒有人分析過這一點,或者也有看可能是我搜索不夠全面,畢竟在網上搜索SMB以及掛載點時,得到的結果大多與SAMBA配置有關。有時候系統出于安全考慮,不會公開某些處理邏輯,我們可以大膽假設小心求證,這個例子就是非常典型的一次實驗。雖然MKLINK
之類的工具提示我們無法設置遠程掛載點,但進一步分析、查看代碼后,我們自己可以找到更為有趣的一些細節。