概述
◆2020年3月10日是微軟補丁日,安全社區注意到Microsoft發布並立即刪除了有關CVE-2020-0796的信息;
◆2020年3月11日早上,Microsoft發布了可糾正SMBv3協議如何處理特製請求的修補程序;
◆2020年03月12日微軟發布安全公告聲稱Microsoft 伺服器消息塊 3.1.1 (SMBv3) 協議處理某些請求的方式中存在遠程執行代碼漏洞。成功利用此漏洞的攻擊者可以獲取在目標伺服器或客戶端上執行代碼的能力。要利用針對伺服器的漏洞,未經身份驗證的攻擊者可以將特製數據包發送到目標 SMBv3 伺服器。要利用針對客戶端的漏洞,未經身份驗證的攻擊者將需要配置惡意的 SMBv3 伺服器,並說服用戶連接到該伺服器。此安全更新通過更正 SMBv3 協議處理這些特製請求的方式來修復此漏洞。
◆此缺陷可影響SMB協商中的客戶端和服務端。服務端漏洞位於srv2.sys中,客戶端漏洞位於mrxsmb.sys中,這兩個漏洞最終都在SmbCompressDecompress中調用了相同的代碼。
本文試以CVE-2020-0796為例,為讀者 「高清」還原漏洞分析工作視角。
受影響的系統
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)
分析
首先我們來執行CVE-2020-0796的PoC
如果目標系統未處於調試狀態,我們將觀察到目標設備進入藍屏狀態。待Windows系統重啟後,我們會使用WinDBG打開C:\Windows\System32\MEMORY.DMP文件,通過分析內存轉儲文件嘗試找到觸發藍屏的原因。
如果目標系統處於調試狀態,將會在WinDBG中觀測到如下圖所示的中斷:
釋放內存的錯誤
無論是任何一種情況,大多時候在WinDBG中首選執行!analyze -v,嘗試由WinDBG自動分析導致問題的模塊。
或者查看棧回溯
如上文0x0C號棧幀所示,srvnet模塊中的SmbCompressionDecompress函數在調用ExFreePool時是觸發藍屏的直接因素。
同時,我們注意到上文0x0D號棧幀所示的返回函數是模塊名+偏移量的形式,這是因為WinDBG沒有加載srv2模塊的的符號文件。加載srv2模塊的符號之後,棧回溯更有可讀性:
根據函數名稱字面理解或參考DDK文檔ExFreePool是釋放內存的函數,一般不會有什麼問題。這個涉及Windows內核的Pool內存管理機制及結構。過往經驗告訴我們,ExFreePool需要操作的內存結構被破壞掉了,即這可能是個Windows內核中的內存破壞漏洞(Memory Corruption)。
人生終極三問:你是誰?從哪裡來?到哪裡去?在漏洞分析領域同樣適用。
為#FormatImgID_6#搞明白ExFreePool要釋放的內存,來自哪裡,又是被誰搞壞的。我們需要在IDA Pro中看看srvnet模塊中的SmbCompressionDecompress函數。
當然如果你那邊IDA Pro顯示的和上圖所示不同,沒有這些可讀性較好的變量名,而是像下圖這樣
也不必驚訝,後續我們會解釋,如何通過公開的文檔、符號文件或者數據流,註解IDA Pro函數名或者變量名,使得顯示更加友好,以便開展分析工作。這個過程有點像Windows系統自帶的掃雷遊戲。
IDA Pro顯示srvnet模塊中的SmbCompressionDecompress函數主要流程十分清晰:申請內存(ExAllocatePoolWithTag)、解壓處理(RtlDecompressBufferEx2)、釋放內存(ExFreePoolWithTag)。
我們現在已知藍屏的直接原因是釋放內存的操作引起的,那麼問題就顯然出現在成功申請內存之後,到釋放內存之間的這個過程中。我們看到這個過程中只有一個處理函數,即RtlDecompressBufferEx2。
現在所有的疑點都集中在了RtlDecompressBufferEx2函數上,
我們來看看這個ntoskrnl模塊中的RtlDecompressBufferEx2函數。
IDA Pro顯示RtlDecompressBufferEx2函數是根據參數CompressionFormat的一個跳轉函數。
RtlDecompressBufferProcs數組前2個QWORD元素為0。即當CompressionFormat取值為3時,函數最終轉向RtlDecompressBufferXpressLz函數中。
IDA Pro顯示RtlDecompressBufferXpressLz函數是一個300多行偽代碼的複雜函數。
靜態分析有點吃力,為了快速定位問題,讓我們來試試用WinDBG動態調試一下。
還是執行PoC,windbg中斷時執行kn或者!analyze -v。這次我們試試!analyze -v。
太棒了,我們和WinDBG達成了共識。它直接提示可能是nt!RtlDecompressBufferXpressLz+2d0處出了問題。
現在我們了解到nt!RtlDecompressBufferXpressLz+2d0處是一個內存複製函數qmemcpy。這符合往常的漏洞構成的元素。
我們需要再了解一下qmemcpy裡面的這3個參數。
我們設置一個這樣的斷點:
可得我們感興趣的qmemcpy的3個參數:
查看一下目的內存的pool信息:
這是一個0x1280大小的非分頁池內存。qmemcpy函數準備向其中寫入0x8483ffff大小的數據。很顯然會溢出。
對於Pool內存的大小不超過一個頁面長度(PAGE_SIZE,即4K字節)時,可以通過使用POOL_HEADER結構體來查看pool塊信息。
我們注意到0xffffe402ec9f4000之後在ffffe402ec9f5280 處是一個0x700大小的空閒塊,再之後ffffe402ec9f5990 處是一個0x290 大小的已被分配使用的塊。
在qmemcpy函數執行後,我們發現ffffe402ec9f5280處的_POOL_HEADER確實被寫入了數據。
複製數據的大小
現在我們需要搞明白,複製數據大小和目的地址的來源。
經過類似的斷點和調試,我們在nt!RtlDecompressBufferXpressLz+0x2AA處,觀察到qmemcpy中的count數據來自於RtlDecompressBufferXpressLz收到的參數CompressedBuffer的最後4個字節與3的和。因此操作壓縮數據末尾的4個字節,可以控制複製數據的大小。
複製數據大小的來源已經清楚了,就剩下最後一個謎團--目的地址的來源。
目的地址的來源
我們根據設置的WinDBG斷點日誌,整理了上圖所示的函數調用及數據傳遞過程。也順便介紹了前文所述的如何通過公開的文檔、符號文件或者數據流,註解IDA Pro函數名或者變量名,使得顯示更加友好,以便開展分析工作。入手點是https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-rtldecompressbufferex2查閱到的關於RtlDecompressBufferEx2的定義或NT之前洩露的源碼中的相關函數定義。
從日誌上來看,qmemcpy的目的地址正是UncompressedBuffer偏移1的地方。
Srv2DecompressData+0x85處的ExAllocatePoolWithTag() 返回值是0xffffa28f92503000,位於UncompressedBuffer之後0x370CBC8的位置。
即qmemcpy寫入數據大小範圍內有其他的Pool塊時,將會導致ExFreePoolWithTag()時出錯。
任意地址寫入
如果size大小合適或者其範圍內沒有在用的Pool塊,如0x1100+0n24大小時,則會有下述情況:
我們根據相關函數調用,繪製了上圖所示的內存布局圖。
當srv2!Srv2DecompressData+0x79處 SrvNetAllocateBuffer((unsigned int)(hdr.OriginalCompressedSegmentSize + offset)申請內存時,返回值設定AllocateBuf,簡稱A點。B點至U點正是SMB協議頭中的offset值0x03e8(0n1000)。
OriginalCompressedSegmentSize值(Wireshark中所示的OriginalSize)過大,與offset相加導致整數溢出。最終申請了一個較小的內存。即B點至A點的內存。內存的起始地址被寫在AllocateBuf+0n24的P點。
當解壓函數把超量數據寫入U點時,如果超過了之前申請的內存(B點至A點的內存),也會覆蓋原本存放在P處的指針。
srv2!Srv2DecompressData+0x108處的memmove會讀取P點的指針作為目的地址,寫入原始數據中offset之前的數據,從而完成預定的解壓邏輯。當P處的指針可以被改寫後,攻擊者就獲得了一次任意地址寫入任意數據的能力。
至此漏洞分析視角下的工作基本完成,撰寫分析報告時,我們會用倒敘的方法,就是大家經常看到的文章形式。後續文章我們再談談漏洞補丁分析和漏洞利用。
解決方案
儘快安裝微軟官方補丁或在網絡出入口上阻止TCP埠445,以防止SMB流量進出網際網路。此外,我們建議您進行內部網絡分段,並禁止終端之間的SMB連接,以防止橫向移動。
禁用SMBv3壓縮將防止利用易受攻擊的SMB伺服器。要禁用SMBv3壓縮,可以在PowerShell中運行以下命令:
綜述
藍屏(BSOD)一般是遠程代碼執行的前兆,從其進化到遠程代碼執行會更具挑戰性,因為需要藉助其他漏洞以便繞過Windows最新的緩解技術(KASLR、KARL)。此漏洞對攻擊者具有很高的價值,可使得攻擊者很容易觸及分配內存的函數,並且可以控制觸發溢出的數據大小。另一方面,攻擊者輸入的對象的內存被很快釋放,使漏洞利用更加困難。
參考 &引用
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962
http://yiiyee.cn/blog/2013/12/11/large-pool-1/
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-rtlgetcompressionworkspacesize
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-rtldecompressbufferex2
命令&斷點