取證分析之 OpenSSH Session 信息解密
不久前,我們遇到了一個取證案例,一個Linux服務器被攻破,一個修改過的OpenSSH二進制文件被加載到一個web服務器的內存中。修改后的OpenSSH二進制文件被攻擊者用作系統的后門。在系統遭到破壞時,客戶擁有pcap和系統的虛擬機監控程序截圖。根據這個發現,我們開始懷疑是否有可能通過從內存截圖中恢復密鑰材料來解密SSH session并獲得有關它的知識。
在本文中,我將介紹我對OpenSSH所做的研究,并發布一些從內存中轉儲OpenSSHsession密鑰的工具,并結合使用pcap解密和解析session,另外我還向2020 Volatility framework plugin contest提交了我的研究成果。
SSH協議
首先,我會閱讀OpenSSH及其工作原理。幸運的是,OpenSSH是開源的,因此我們可以輕松下載和閱讀實現細節。RFC的內容,雖然讀起來有點枯燥,但也包含了豐富的信息。從較高的層次來看,SSH協議如下所示:
(1) SSH協議+軟件版本交換;
(2) 算法協商(KEX INIT):
- 密鑰交換算法;
- 加密算法;
- MAC算法;
- 壓縮算法;
(3) 密鑰交換;
(4) 用戶認證;
(5) 客戶請求“session”類型的頻道;
(6) 客戶端請求一個偽終端;
(7) 客戶端與session進行交互;
一開始,客戶端就會連接到服務器并發送協議版本和軟件版本:
SSH-2.0-OpenSSH_8.3,服務器以其協議和軟件版本進行響應。交換初始協議和軟件版本后,所有流量都封裝在SSH框架中。SSH框架主要存在于框架的長度、填充長度、有效載荷數據、填充內容和MAC中。 SSH框架示例如下:
使用dissect.cstruct解析的SSH框架示例
在協商加密算法并生成session密鑰之前,SSH框架將不被加密,并且即使加密了該框架,根據算法的不同,部分框架可能也不會被加密。例如,aes256-gcm不會對框架中的4個字節長度進行加密,但是chacha20-poly1305會進行加密。
接下來,客戶端將向服務器發送KEX_INIT消息,以開始協商session的參數,例如密鑰交換和加密算法。根據這些算法的順序,客戶端和服務器將選擇雙方都支持的第一個首選算法。在KEX_INIT消息之后,交換幾個與密鑰交換相關的消息,然后雙方互相發送NEWKEYS消息。這個消息會告訴另一方一切都準備好開始加密session,并且流程中的下一個框架將被加密。在雙方都獲得新的加密密鑰生效后,客戶端將請求用戶身份驗證,并根據服務器上配置的身份驗證機制執行基于密碼/密鑰/等的身份驗證。通過session身份驗證后,客戶端將打開一個通道,并根據請求的操作(ssh/ sftp/ scp等)通過該通道請求服務。
恢復session密鑰
恢復session密鑰的第一步是分析OpenSSH源代碼并調試現有的OpenSSH二進制文件,我嘗試自己編譯OpenSSH,將生成的session密鑰記錄在某個地方,并附加一個調試器,然后在程序的內存中搜索這些密鑰。成功以后,session密鑰保存在堆中的內存中。對源代碼的更多深入研究使我了解了負責發送和接收NEWKEYS框架的函數。我發現有一個存儲“session_state”結構的“ssh”結構,該結構又包含與當前SSHsession有關的所有信息,包括一個newkeys結構,其中包含與加密、mac和壓縮算法有關的信息。經過深入研究,我們最終找到了包含密碼名稱、密鑰、IV和塊長度的“shenc”結構。OpenSSH的結構概述如下所示:
SSHENC的結構和關系
以及shenc結構的定義:
SSHENC結構
很難在內存中找到密鑰本身(因為它只是一個隨機字節的字符串),但是shenc和其他結構更加獨特,具有一些我們可以驗證的屬性。然后,我們可以抓取程序的整個內存地址空間,并驗證針對這些約束的每個偏移量。我們可以檢查以下屬性:
- 名稱,密碼,密鑰和iv成員都是有效的指針;
- name成員指向一個有效的密碼名,該密碼名稱等于cipher-> name;
- key_len在有效范圍內;
- iv_len在有效范圍內;
- block_size在有效范圍內;
如果我們針對所有這些約束進行驗證,那么應該能夠可靠地找到shenc結構。為此,我開始構建一個POC Python腳本,該腳本可以在一個實時主機上運行,該主機連接到進程并為該結構勾勒出內存。可以在這里找到此腳本的源代碼,它實際上工作得很好,并為找到的每個密鑰輸出一個json blob。因此,我演示了可以使用Python和ptrace從活動主機中恢復session密鑰,但是我們如何從內存截圖中恢復session密鑰呢?這就是Volatility發揮作用的地方。Volatility是一個用Python編寫的內存取證框架,可以編寫自定義插件。經過一些努力,我能夠編寫Volatility 2插件,并且能夠分析內存截圖并轉儲session密鑰!對于Volatility 3 plugin contest ,我還將該插件移植到了Volatility 3,并提交了該插件并進行了研究。
Volatility 2 SSH Session密鑰轉儲器的輸出結果
解密和解析流量
用于加密和解密通信的session密鑰的恢復是成功的,接下來是解密通信流量!我開始用pynids(TCP解析和重組庫)解析一些pcap。我使用內部開發的dissect.cstruct庫解析數據結構,并開發了解析框架來解析ssh等協議。解析框架基本上以正確的順序將數據包送入協議解析器,因此,如果客戶端發送2個數據包,而服務器響應3個數據包,則這些數據包也將以相同的順序提供給解析器。這對于保持整體協議狀態很重要,解析器基本上使用SSH框架,直到遇到NEWKEYS框架為止,這表明下一框架已加密。現在,解析器從該源中窺視流中的下一框架,并迭代提供的session密鑰,以嘗試解密該框架。如果成功,解析器將以該狀態安裝session密鑰以解密session中的其余框架,解析器幾乎可以處理OpenSSH支持的所有加密算法,具體過程可以查看以下動圖:
SSH協議解析
最后是運行中的解析器,你可以在其中看到解密和解析SSHsession的過程,還暴露了用戶用于身份驗證的密碼:
解密和解析的SSHsession示例
總結
本文我研究了SSH協議,以及如何將session密鑰存儲并保存在OpenSSH的內存中,并找到了一種從內存中提取它們并在網絡解析器中使用它們來解密SSH會話并將其解析為可讀輸出的方法。本研究中使用的腳本可以在以下鏈接中找到:
- 將Python POC轉儲為SSHsession密鑰;
- Volatility 2插件;
- Volatility 3插件;
- SSH協議解析器;
- 更好的選擇將是在Wireshark中實現這個解密器和解析器。
有趣的是,在研究期間,我還在OpenSSH源代碼的ssh_set_newkeys函數中遇到了這些注釋行。其實這很諷刺,如果不對這些行進行注釋并在OpenSSH二進制文件中進行編譯,那么這項研究就會困難得多。
OpenSSH源代碼片段
本文翻譯自:https://research.nccgroup.com/2020/11/11/decrypting-openssh-sessions-for-fun-and-profit/
【責任編輯:趙寧寧 TEL:(010)68476606】