工具雖老,知識卻不老。
這只是一個瑣碎的細節,純當筆記在寫。如有錯誤,還請各位指點。
預設場景
三臺機器:
1. PC-1
2. PC-2
3. PC-3
非域環境(其實域環境下也類似),三臺機器上 Administrator 的密碼全是 123456
需要知道的背景知識
- NTLMSSP 的基礎知識(略過)
- Windows 核心編程的一些基礎
- PsExec 遠程執行的大概原理
- PsExec 利用某一個賬號,通過 SMB 協議連接遠程機器的命名管道(基于 SMB 的 RPC),在遠程機器上創建并啟動一個名為 PSEXESVC 的服務
- PSEXESVC 服務會注冊新的命名管道
- PsExec 連接至 PSEXESVC 新創建的命名管道,通過此管道通知 PSEXESVC 服務在遠程機器上面啟動我們指定的程序,并將程序的 stdin 與 stdout 通過命名管道轉回本地 Shell,現實遠程命令的本地交互(是不是有點像反彈 Shell?)。
上面說到 “PsExec 利用某一個賬號”與遠程機器進行身份驗證,這里的某一個賬號有可能是:
- 執行 psexec 時利用 -u 與 -p 參數手動指定的賬號密碼
- 當沒有手動指定賬號時,則利用當前用戶的憑據信息(與 psexec 進程相關聯的 token 中的憑據信息)
(實際根據 PsExec 版本的不同,有時就算手動指定了某個賬號,PsExec 依然會先嘗試利用當前用戶的憑據去進行身份驗證)
測試 PsExec - 第一回
以 Administrator 登陸 PC-1,然后:
1.psexec \PC-2 cmd.exe
沒有給 psexec 指定賬號密碼,psexec 會利用當前用戶的憑據成功從 PC-1 連接到 PC-2,并獲取到了一個可以偽交互的 cmd shell。
2.隨后在 cmd shell 中執行:
psexec \\PC-3 hostname
嘗試利用 PC-2 的 cmd shell 去連接 PC-3,并執行 hostname,期待能夠看到 hostname 命令的輸出,也就是:PC-3。
但是,失敗了。
我本地的錯誤信息是:句柄無效。
為什么會失敗呢?
測試 PsExec - 第二回
以 Administrator 登陸 PC-1,然后:
1.psexec \PC-2 -u administrator -p 123456 cmd.exe
雖然通過第一回測試我們知道就算不手動指定 adminstrator 賬號與密碼也可以連接成功,但是這里我們還是指定吧。
成功從 PC-1 連接到了 PC-2,并獲取到了一個可以交互的 cmd shell。
2.隨后在 cmd shell 中執行:
psexec \\PC-3 hostname
3.成功從 PC-2 連接到了 PC-3,并執行了 hostname 命令,并成功得到回顯:PC-3
為什么第一回的測試中,從 PC-2 連接至 PC-3 執行命令失敗了,但在第二回的測試中可以成功呢?
在我的認知中,第一回確實是應該失敗的。而且我認為,如果第一回失敗,那么第二回的測試也應該失敗才對。因為太長時間沒有過用 PsExec 了,所以我自己在看到第二回測試成功的時候,詫異了一下,它與我的認知相悖了。
第一回為什么應該失敗?
PC-1 上通過 psexec \PC-2 cmd.exe 連接至 PC-2 后,我猜測 PC-2 上的 PSEXESVC 通過類似 ImpersonateNamedPipeClient 的函數 impersonate 了客戶端的身份(也就是 Administrator)。后面映證了我的猜測是正確的。
impersonate 分為兩個級別:
-
impersonation。此級別的 impersonate,使得服務端可以使用客戶端的身份來訪問本地資源,但是無法訪問網絡資源,這也被稱為 SSP 的 double hop 的問題。(想了解更多的 Google "authentication double hop")
-
delegation。此級別的 impersonate,使得服務端可以使用客戶端的身份訪問本地資源以及網絡資源。
在微軟提供的所有 SSP 中,只有 Kerberos 是支持 delegation 級別的 impersonate 的(MSDN 里面是這么說的),而且還需要在域控上進行特別的配置才可以。
所以,我猜測 PC-2 上的 PSEXESVC 對客戶端進行了 impersonation 級別的 impersonate(事實也無法進行 delegation 級別的 impersonate,因為 NTLMSSP 不支持),所以它無法訪問網絡資源,自然也就無法再去遠程訪問 PC-3。
“無法再去遠程訪問 PC-3” 具體體現在,在 PC-2 上嘗試遠程連接 PC-3 的時候,與 PC-3 進行的是空會話認證,也就是所謂的 anonymous logon,即匿名登陸:

可以看到在上圖的 NTLMSSP-TYPE 3 消息中, Negotiate Anonymous 標志位被設置為 1,且 Domain name/User name/NTLM Response 處的值均為空。
第二回為什么成功了?
第二回的測試成功了讓我很詫異,我還以為我一直認為 “NTLM 無法 delegation” 這件事是錯的。然后我去翻了一下 PsExec 的幫助文檔,以及 PsExec 作者寫的文章,才得以解惑。(PsExec 的作者是 Microsoft Azure 的 CTO!)
以下為摘錄:
When you specify a username the remote process will execute in that account, and will have access to that account's network resources.
If you omit username the remote process will run in the same account from which you execute PsExec, but because the remote process is impersonating it will not have access to network resources on the remote system.
If you do specify an alternative username/password, then PsExec will send the password in clear text. This can be a security risk if unauthorized network sniffers could intercept traffic between the local and remote system.
簡單翻譯就是:
如果在運行 psexec 的時候,沒有指定用戶名與密碼,則 psexec 會使用當前的身份與遠程機器進行驗證。驗證成功后服務端會 impersonate 客戶端,此時無法通過遠程機器訪問任何的網絡資源。(這里對應的第一回的測試情況,說明我的猜測是沒錯的)
如果手動指定了用戶名與密碼,則在驗證通過后,PsExec 會將賬號密碼以明文的方式發送至遠端服務器(從 2.1 版本開始,不再是明文發送)。通過這段描述,大概能猜到在手動指定了用戶名與密碼的情況下,PSEXESVC 不再是利用 impersonate 來獲取客戶端身份,而是通過其他的方式。我想這里解釋了為什么第二回的測試是成功的。
我猜測,第二回的過程應該是這樣的(網上好像沒有 psexec 的源代碼,而且我沒有逆向能力,所以只能猜測了,我估計我猜的八九不離十):
- PsExec 通過當前用戶的憑據或者我們手動指定的憑據(根據 psexec 版本的不同)連接至遠程機器,創建并啟動 PSEXESVC 服務。
- PSEXESVC 服務會創建新的命名管道
- PsExec 連接至 PSEXESVC 創建的管道,將我們指定的賬號、密碼、以及要執行的命令發送至管道
- PSEXESVC 從管道處接收到我們發送的賬號、密碼、要執行的命令等信息
- PSEXESVC 將賬號、密碼傳入 LogonUser 類的函數,在遠程機器上創建了一個新的 logon session。新的 logon session 就代表著一個新的 token。并以這個 token 啟動了我們指定的程序,也就是 cmd.exe
- 新的 token 中帶有我們傳入的憑據,所以在 PC-2 的這個 cmd shell 中再去執行 psexec \PC-3 hostname,會利用 token 中的憑據與 PC-3 進行驗證。驗證成功。
PsExec 通過將憑據發送至遠程機器,避開了 NTLM 無法 delegation 的問題(也就是 double hop)的問題。如果你了解 CredSSP 的話,你會發現 PsExec 的這種做法與 CredSSP 非常相似。
參考資料
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/503/
暫無評論