一、漏洞背景
2021年7月13日,美國微軟威脅情報中心發布安全公告[1],文中指出黑客利用Serv-U 0day對極少數美國軍工部門成功進行了攻擊,同日Serv-U母公司solarwinds也發布安全公告[2],并針對最新大版本發布了補丁[3],
(注意:目前發布的補丁只針對最新的15.2.3大版本,且只能是付費用戶才能下載安裝補丁,非付費用戶目前無法從官方渠道獲取有效補丁,)
根據補丁比較和fuzzer,寧靜之盾安全團隊成功復現了該遠程溢位漏洞,并能在實戰環境下成功利用,
截至9月10日,互聯網上未發現任何有關該漏洞的技術分析和POC發布,也未發現更多新聞細節公布,
該漏洞是2003年Serv-U mdtm漏洞修補后出現的第一個RCE漏洞,
為了感謝廣大讀者伙伴的支持,準備了以下福利給到大家:
[一>獲取<一]
1、200多本網路安全系列電子書(該有的都有了)
2、全套工具包(最全中文版,想用哪個用哪個)
3、100份src原始碼技術檔案(專案學習不停,實踐得真知)
4、網路安全基礎入門、Linux、web安全、攻防方面的視頻(2021最新版)
6、 網路安全學習路線(告別不入流的學習)
7、ctf奪旗賽決議(題目決議實戰操作)
[一>獲取<一]
二、Serv-U軟體說明
該軟體是全球流行的商業閉源FTP服務器端軟體,官方資料顯示該軟體全球有超過10W用戶,軟體默認支持ftp(21埠)/sftp(22埠)等,出于安全性考慮現在大部分使用的是sftp埠(流量使用非對稱演算法加密),主要運行在windows平臺,新版本為X64程式,該軟體22埠默認banner資訊含有詳細版本號,通過zoomeye搜索顯示近一年IP量為13000個(全量為6.7W多),

其中15.2.3大版本有1100臺,最新補丁版本15.2.3.742有900臺,該漏洞影響范圍是Serv-U 版本 < 15.2.3 HF2(即15.2.3.742),按照近一年存活13000臺,已安裝此漏洞補丁900臺計算,也就是說現在全網至少有90%的Serv-U處于受此漏洞威脅狀態(Serv-U母公司應該是出于商業考慮未對大量存在的老版本發布補丁,老版本升級到最新版本需要再次付費),


Banner資訊
三、漏洞分析與利用說明
綜述
該漏洞是一個遠程記憶體破壞漏洞(可以穩定控制虛函式指標),針對WINDOWS版Serv-U SFTP(22埠)進行攻擊,該漏洞無需任何賬號和密碼,輸入IP和埠即可成功攻擊,只要版本低于15.2.3.742,成功率接近100%(單次成功率達不到100%,但可以立即再次攻擊),利用成功獲取SYSTEM權限SHELL,

3.1補丁比較
下載最新版本742和上一版本723,補丁包檔案如下
> 15.2.3.723 hf1 5/14/2021
>
> Serv-U crashes due to incorrect OpenSSL API usage
>
> <Serv-U-InstallDir>\Serv-U.dll
>
> <Serv-U-InstallDir>\RhinoNET.dll
>
> 15.2.3.742 hf2 7/12/2021
>
> * Unauthenticated Remote Code Execution in SSH protocol
>
> * <Serv-U-InstallDir>\Serv-U-RES.dll
>
> * <Serv-U-InstallDir>\Serv-U-Tray.exe
>
> * <Serv-U-InstallDir>\Serv-U.dll
>
> * <Serv-U-InstallDir>\Serv-U.exe

可以看到主要修改了4個檔案,使用Bindiff依次對這4個檔案進行比較,最終可以排除掉Serv-U.exe、Serv-U-Tray.exe和Serv-U-RES.dll,因為這三個檔案改動非常小,基本上不可能存在漏洞,

同時結合官方的報告,APT攻擊中利用該漏洞可能會報如下錯誤:
> EXCEPTION: C0000005;
>
> CSUSSHSocket::ProcessReceive();
>
> Type: 30;
>
> puchPayLoad = 0x041ec066;
>
> nPacketLength = 76;
>
> nBytesReceived = 80;
>
> nBytesUncompressed = 156;
>
> uchPaddingLength = 5
基本可以判斷該漏洞是一個SSH相關的記憶體型漏洞,
使用BINDIFF和IDA對主DLL的補丁情況比較如下:

左邊為未補丁變化的函式地址,右邊為補丁后的函式地址

對Serv-U.dll中的十余個有變化的函式逐個分析,排除掉4個比較錯誤的函式,一共有9個函式發生變化,發現補丁為某些函式添加了硬編碼的判斷與賦值,比如,判斷某個值198h是否為0、1、2、3、4或5等,或者給198h賦值為0、1、2、3、4或5等,

此時,需要配合IDA嘗試理解這些值的含義,首先定位到該值存盤的位置,可以看到該值被存盤到a1[0x198],

然后二進制搜索0x198,找到該值被讀寫的所有位置,

遺憾的是,即使知道了補丁補的位置與代碼,也很難揣測補丁的最終意圖,于是另辟蹊徑,從打過補丁的函式一直向上回溯,依次記錄分析,發現函式sub_180145070會參考所有打過補丁的函式,
其它幾個變化的函式主要是該函式的switch case分支里呼叫的函式,下面我們具體分析該函式,該函式其實是RhinoNET!CRhinoSocket::ProcessReceiveBuffer,通過命名可以猜到是用于處理收到的資料BUF,在該函式入口點下斷:

180145070的呼叫堆疊如下:
RhinoNET!CRhinoSocket::OnReceive+0x170
mfc140u!CWnd::OnWndMsg+0xba9
mfc140u!CWnd::WindowProc+0x3f
mfc140u!AfxCallWndProc+0x123
mfc140u!AfxWndProc+0x54
mfc140u!AfxWndProcBase+0x49
USER32!UserCallWinProcCheckWow+0x1ad
USER32!DispatchMessageWorke+0x3b5
Serv_U_180000000!CUPnPNotifyEvent::SetTimeout+0x30d85
Serv_U_180000000!CUPnPNotifyEvent::SetTimeout+0x30dfd
ucrtbase!crt_at_quick_exit+0x7d
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d
仔細觀察該函式的結構,一個外部大回圈加內部的switch…case,明顯是某種協議的實作,配合關鍵字串SSH_MSG_IGNORE,不難得出結論:
函式sub_180145070實作了部分或完整的SSH握手協議,

通過除錯可知,該函式主要用于處理SSH訊息,依次處理的MSG訊息如下:
> 180145070依次處理的MSG(10進制)
>
> 20 SSH_MSG_KEXINIT
>
> 30 SSH_MSG_ECDH key exchange init ! CASE 28
>
> 21 SSH_MSG_NEWKEYS
>
> 5 SSH_MSG_SERVICE_REQUEST
>
> 50 SSH_MSG_USERAUTH_REQUEST
>
> 90 SSH_MSG_CHANNEL_OPEN
>
> 98 SSH_MSG_CHANNEL_REQUEST
>
> 94 SSH_MSG_CHANNEL_DATA
比如上圖中:switch(v21)中v21就是SSH協議中的訊息碼,標識了訊息型別,

通過此檔案可查到相關SFTP協議的規范
補丁后的程式,主要修補了20,30,21三個MSG,其中在這些MSG中處理CLIENT支持的SSH加密演算法處多呼叫了SSH庫578,530和538函式做檢查(EVP_aes_128_ctr,EVP_EncryptInit_ex和EVP_DecryptInit_ex)
同時通過除錯可知,修補后的程式,對MSG序列的順序做了限制!
補丁比較后基本判斷該漏洞是一個SSH協議握手階段的邏輯處理出錯,進而造成的記憶體處理出錯,那么通過補丁比較就找不到傳統記憶體溢位型漏洞補丁一般會對COPY長度做限制的地方,要復現這個漏洞就需要對Serv-U的SSH握手程序進行FUZZER,
3.2 FUZZER SSH握手程序
有了上述的資訊,便可以寫一個發包FUZZ腳本(不斷生成訊息碼去測驗),通過模擬SSH握手階段的資料交換來模糊測驗Serv-U服務器15.2.3.723,

腳本運行一段時間后,得到了一個崩潰,通過分析該崩潰可以確認我們發送完20號訊息后,不發送30號MSG,直接發送21號訊息,是造成Serv-U崩潰的原因,打上補丁后不會產生該問題,那么說明很可能找到了漏洞點,
同時該漏洞大概率能夠利用,因為Serv-U.dll沒有開啟ASLR,而我們擁有控制EIP的能力,所以配合ROP便可以遠程執行代碼了,
如下圖,可以看到被呼叫的函式指標被覆寫為了AAAAAA……,也就是我們可以利用可控的資料覆寫一個虛函式指標,從而控制函式執行流程以執行我們的ShellCode,

崩潰時的呼叫堆疊如下:

崩潰的最終位置并不在Serv-U.dll里,而是在libeay32.dll,這個元件是OpenSSL用于加解密的一個組件,通過跟蹤除錯,發現握手資料AAAAAA…的最后8位元組資料被libeay32.dll錯誤地當作函式指標來呼叫,從而觸發了崩潰,

定位崩潰時發送的資料包,發現該資料包打亂了SSH正常的通信程序(握手包亂序),所以補丁中的硬編碼值0、1、2、3、4、5等是為限制了資料包的發送順序,即發送了包MSG 20之后只能發包MSG 30,以此緩解該漏洞,
3.3 漏洞利用
通過分析發包流程和Serv-U的SSH通信處理程序便得知,該**漏洞的成因為Serv-U的SSH通信處理流程亂序導致的記憶體未初始化漏洞,**通過網路資料包列舉漏洞觸發原理及程序如下:
- SSH通信正常的處理程序為
密鑰交換初始化(申請N位元組記憶體,每個版本可能不同);
密鑰協商演算法交換引數,比如ECDH(填充N位元組記憶體:在記憶體塊內填充函式地址等);
加解密資料(呼叫N位元組記憶體中的函式地址)

- 觸發漏洞的程序為
密鑰交換初始化(申請N位元組記憶體);
加解密資料(呼叫N位元組記憶體中的函式地址);
可以看到漏洞觸發程序省略了ECDH密鑰交換協商這一步,并且在第一步申請N位元組時并未初始化記憶體,

- 漏洞觸發利用程序
發包占坑布局N位元組記憶體空間,并釋放;
密鑰交換初始化(申請N位元組記憶體,該記憶體內容已被提前布局)
加解密資料觸發漏洞**(由于缺少了密鑰協商設定N位元組記憶體塊內函式指標這一步,所以會呼叫N位元組記憶體中已被提前布局的函式地址);**

- 漏洞補丁原理
漏洞補丁限制了SSH通信程序,所以不會再導致記憶體中函式指標被攻擊者提前布局并利用的情況,
通過分析該崩潰,確定了該漏洞大概率能夠利用,因為Serv-U.dll并沒有開啟ASLR,為我們提供了用于布置ROP鏈的必要條件,除此之外,上述崩潰的位置在CALL指令處,這使得我們大概率可以控制EIP,當同時擁有以上兩種能力后,就可以控制程式跳轉到布置好的ROP鏈上執行預先指定的代碼,

雖然Serv-U.dll中沒有匯入VirtualProtect函式,但是它匯入了另外一個能夠執行代碼的函式ShellExecuteExW,所以目標就是構造ROP鏈傳入指定引數執行ShellExecuteExW,

首先使用capstone撰寫小工具提取ROP,獲取所需指令的地址,

然后拼接這些指令,達到執行ShellExecuteExW的目標,具體步驟是:切換堆疊+布局引數+呼叫函式,

切換堆疊是第一步,但非常簡單,使用xchg、pop、mov以及lea等指令修改rsp即可,布局引數是整個程序中最難的,因為函式ShellExecuteExW只有一個引數,該引數為一個結構體指標,而結構體內部的字串指標才是執行命令的核心,所以這里涉及到多級指標的布控,由于我們控制了整個堆疊,所以布置多級指標的也并不難,但步驟比較繁瑣,容易出錯,一定要耐心謹慎,

引數布置好之后,再次控制EIP跳轉到匯入表內的函式指標執行,一旦函式ShellExecuteExW執行完畢,就代表著用戶指定的命令也已經執行完畢,但由于堆疊已經損壞,主程式將隨之崩潰,
如果不利用ShellExecuteExW也可以利用下圖所示位置的代碼,自行獲得VirtualProtect函式地址:

(上圖地址DLL版本為15.2.3.723)
四、現有補丁說明
截止9月10日,發布的補丁情況如下:
| 版本號 | 發布時間 | 有無漏洞 |
| <15.2.3.717 | ---- | 有 |
| 15.2.3.717 | 2021/04/20 | 有 |
| 15.2.3.723 hf1 | 2021/05/14 | 有 |
| 15.2.3.742 hf2 | 2021/07/12 | 無(最新版) |
五、漏洞利用工具說明
5.1 受影響軟體版本
SSH-2.0-Serv-U_15.1.6.25至SSH-2.0-Serv-U_15.2.3.723
備注:SSH-2.0-Serv-U_15.1.6.25版本為2017年發布,更早的版本肯定也受影響,只不過當前測驗的最早版本為2017年,SSH-2.0-Serv-U_15.2.3.723版本為最新的補丁版本的前一個版本,
5.2 遠程利用限制條件
Serv-U需要以服務方式啟動才能利用成功,不過Serv-U默認安裝本來就是以服務方式啟動的,如果以非服務方式啟動則無法利用成功,

5.3 遠程利用執行演示效果及特殊情況說明
- 利用成功后,目標機器的Serv-U.exe程式會執行我們的ShellCode代碼,并且啟動一個PowerShell子行程(父行程為Serv-U.exe),并通過PowerShell腳本回連我們設定的IP和埠,獲取的SHELL權限為SYSTEM權限;
- ShellCode代碼啟動的程式和執行的程式命令可以通過修改攻擊腳本自行設定;
- 上述第2點提到的ShellCode執行的程式命令有長度限制,不能超過500個位元組;
- 如果利用成功,Serv-U.exe程式會崩潰并自動重啟(不會彈框,因為是服務方式啟動所以會自動重啟),所以可以無限次反復遠程利用;
- 如果利用失敗,攻擊腳本執行完后稍等十秒再進行下一次攻擊,直到成功為止;
- 每次的攻擊成功率大概在20-50%;
- 單次攻擊發出的網路資料包大小在2-3M左右,注意網路情況;
- 每個Serv-U版本需要有對應的ROP序列,所以每一個單獨的小版本的Serv-U都需要定制化開發對應的攻擊代碼(程式有DEP,但關鍵DLL無ASLR),
- 本文僅作安全技術分析,旨在為無法獲得補丁的用戶和安全研究人員提供此漏洞細節分析,所以不提供任何POC,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/321011.html
標籤:其他
上一篇:AWD簡單介紹和搭建AWD平臺
