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