作者:yyjb
原文鏈接:http://noahblog.#/cve-2019-0708lou-dong-zai-server2008r2shang-de-li-yong-fen-xi/

分析背景

cve-2019-0708是2019年一個rdp協議漏洞,雖然此漏洞只存在于較低版本的windows系統上,但仍有一部分用戶使用較早版本的系統部署服務器(如Win Server 2008等),該漏洞仍有較大隱患。在此漏洞發布補丁之后不久,msf上即出現公開的可利用代碼;但msf的利用代碼似乎只針對win7,如果想要在Win Server 2008 R2上利用成功的話,則需要事先在目標機上手動設置注冊表項。

在我們實際的滲透測試過程中,發現有部分Win Server 2008服務器只更新了永恒之藍補丁,而沒有修復cve-2019-0708。因此,我們嘗試是否可以在修補過永恒之藍的Win Server 2008 R2上實現一個更具有可行性的cve-2019-0708 EXP。

由于該漏洞已有大量的詳細分析和利用代碼,因此本文對漏洞原理和公開利用不做贅述。

分析過程

我們分析了msf的exp代碼,發現公開的exp主要是利用大量Client Name內核對象布局內核池。這主要有兩個目的,一是覆蓋漏洞觸發導致MS_T120Channel對象釋放后的內存,構造偽造的Channel對象;二是直接將最終的的shellcode布局到內核池。然后通過觸發IcaChannelInputInternal中Channel對象在其0x100偏移處的虛函數指針引用來達到代碼執行的目的。如圖1:

img

圖1

而這種利用方式并不適用于server2008r2。我們分析了server2008r2的崩潰情況,發現引起崩潰的原因是第一步,即無法使用Client Name對象偽造channel對象,從而布局失敗。這是因為在默認設置下,server 2008 r2的 RDPSND/MS_T120 channel對象不能接收客戶端Client Name對象的分片數據。根據MSF的說明(見圖2),只有發送至RDPSND/MS_T120的分片信息才會被正確處理;win7以上的系統不支持MS_T120,而RDPSND在server 2008 r2上并不是默認開啟的因此,我們需要尋找其他可以偽造channel對象的方式。

img

圖2 MSF利用代碼中針對EXP的說明

在此期間,我們閱讀了幾篇詳細分析cve-2019-0708的文章(見參考鏈接),結合之前的調試分析經歷,我們發現的確可以利用RDPDR 的channelID(去掉MSF中對rdp_on_core_client_id_confirm函數的target_channel_id加一的操作即可)使Client Name成功填充MS_T120 channel對象,但使用RDPDR 有一個缺陷:RDPDR Client Name對象只能申請有限的次數,基本上只能完成MS_T120對象的偽造占用并觸發虛函數任意指針執行,無法完成后續的任意地址shellcode布局。

img

圖3

我們再次研究了Unit 42發布的報告,他們利用之前發布的文章中提到的Refresh Rect PDU對象來完成內核池的大范圍布局(如圖,注:需要在RDP Connection Sequence之后才能發送這個結構)。雖然這種內存布局方式每次只能控制8個字節,但作者利用了一個十分巧妙的方式,最終在32位系統上完成漏洞利用。

img

圖4

在解釋這種巧妙利用之前,我們需要補充此漏洞目前的利用思路:得到一個任意指針執行的機會后,跳轉到該指針指向的地址中,之后開始執行代碼。但在內核中,我們能夠控制的可預測地址只有這8個字節。雖然此時其中一個寄存器的固定偏移上保存有一個可以控制的偽造對象地址,但至少需要一條語句跳轉過去。

而文章作者就是利用了在32位系統上地址長度只有4字節的特性,以及一條極短的匯編語句add bl,al; jmp ebx,這兩個功能的代碼合起來剛好在8字節的代碼中完成。之后通過偽造channel對象里面的第二階段跳轉代碼再次跳轉到最后的shellcode上。(具體參考Unite 42的報告

我們嘗試在64位系統上復現這種方法。通過閱讀微軟對Refresh Rect PDU描述的官方文檔以及msf的rdp.rb文件中對rdp協議的詳細注釋,我們了解到,申請Refresh Rect PDU對象的次數很多,能夠滿足內核池布局大小的需求,但在之后多次調試分析后發現,這種方法在64位系統上的實現有一些問題:在64位系統上,僅地址長度就達到了8字節。我們曾經考慮了一種更極端的方式,將內核地址低位上的可變的幾位復用為跳轉語句的一部分,但由于內核池地址本身的大小范圍,這里最多控制低位上的7位,即:

0xfffffa801“8c08000“ 7位可控

另外,RDPDR Client Name對象的布局的可控數據位置本身也是固定的(即其中最低的兩位也是固定的),這樣我們就只有更少的5位來實現第二階段的shellcode跳轉,即:

“8c080”0xfffffa801“8c080”00 5位可控,

由于偽造的channel對象中真正可用于跳轉的地址和寄存器之間仍有計算關系,所以這種方法不可行,需要考慮其他的思路。

把利用的條件和思路設置得更寬泛一些,我們想到,如果目前rdp協議暫時不能找到這樣合適的內核池布局方式,那其他比較容易獲取的也比較通用的協議呢?結合以前分析過的協議類的多種代碼執行漏洞,smb中有一個用得比較多的內核池布局方式srvnet對象。

無論是永恒之藍還是之后的SMBGhost都使用到srvnet對象進行內存布局。最容易的方法可以借助于msf中ms17-010的代碼,通過修改代碼中對make_smb2_payload_headers_packetmake_smb2_payload_body_packet 大小和數據的控制,能夠比較容易地獲取一種穩定的內核池布局方式(相關代碼參考圖5)。

img

圖5

由于單個Client Name Request所申請的大小不足以存放一個完整的shellcode,并且如上面提到的,也不能申請到足夠多的RDPDR Client Name來布局內核池空間,所以我們選擇將最終的shellcode直接布局到srvnet申請的內核池結構中,而不是將其當作一個跳板,這樣也簡化了整個漏洞的利用過程。

最后需要說明一下shellcode的調試。ms17-010中的shellcode以及0708中的shellcode都有一部分是根據實際需求定制的,不能直接使用。0708中的shellcode受限于RDPDR Client Name大小的限制,需要把shellcode的內核模塊和用戶層模塊分為兩個部分,每部分shellcode頭部還帶有自動搜索另一部分shellcode的代碼。為了方便起見,我們直接使用ms17-010中的shellcode,其中只需要修改一處用來保存進程信息對象結構的固定偏移地址。之后,我們仍需要在shellcode中添加文章中安全跳過IcaChannelInputInternal函數剩余部分可能崩潰的代碼(參考*Patch Kernel to Avoid Crash* 章節),即可使整個利用正常工作。64位中添加的修補代碼如下:

mov qword ptr[rbx+108h],0
mov rax,qword ptr[rsp]
add rax,440h
mov qword ptr[rsp],rax
mov r11,qword ptr gs:[188h]
add word ptr [r11+1C4h],1

總結

本篇文章主要是分享我們在分析CVE-2019-0708漏洞利用的過程中整合現有的一些方法和技術去解決具體實際問題的思路。但這種方法也會有一些限制,例如既然使用了smb協議中的一些內核對布局方式,則前提是需要目標開啟了smb端口。另外,不同虛擬化平臺下的目標內核基址需要預測來達到使exp通用的問題仍沒有解決,但由于這個漏洞是2019年的,從到目前為止眾多已經修補過的rdp信息泄露漏洞中泄露一個任意內核對象地址,應該不會是太難的一件事。

綜上,我們建議用戶盡量使用最新的操作系統來保證系統安全性,如果的確出于某些原因要考慮較早版本且不受微軟安全更新保護的系統,也盡量將補丁打全,至少可以降低攻擊者攻擊成功的方法和機會。

參考鏈接


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1700/