CVE-2021-22911-Rocket.Chat 遠程命令執行從補丁對比到漏洞全景分析
漏洞說明
`Rocket.Chat`是一個開源的完全可定制的通信平臺,由`Javascript`開發,適用于具有高標準數據保護的組織。2021年3月19日,某高危漏洞在`HackerOne`被披露,并于2021年4月14日被官方修復,根據CVE信息,該漏洞可以實現特定條件遠程命令執行。漏洞在安全人員分析`3.12.1`版本代碼時被發現,在`3.13.2`、`3.12.4`和`3.11.4`版本被修補。

環境安裝
使用ubuntu-20.04操作系統,根據官網安裝界面顯示,`Rocket.Chat`支持多種安裝方式。

從下面地址下載:
Rocket.Chat
?https://github.com/RocketChat/Rocket.Chat/releases
下載后手動編譯安裝,安裝參考如下所示,這里不在贅述。
安裝參考
https://docs.rocket.chat/quick-start/installing-and-updating/manual-installation/ubuntu
補丁對比
下載`1.13.1`和`1.13.2`版本進行補丁對比。
第一處修改為`app\api\server\helpers\parseJsonQuery.js`。針對`/api/v1/users.list`接口,添加`pathAllowConf`設置。

主函數調用`clean`過濾函數:

第二處修改為`app\livechat\server\methods\loadHistory.js`。

第三處修改為`server\methods\getPasswordPolicy.js`,添加了token檢查。

漏洞分析
0x01 Rocket.Chat框架簡要分析

對于路由映射,首先捕獲登錄后報文,大部分接口為`method.call`:

在`app\api\server\v1\misc.js`中添加`method.call`和`method.callAnon`路由,其中`callAnon`無需認證。

未授權的API直接使用`/api/v1/method.callAnon`加上方法名即可,一個典型的登錄報文如下所示。

0x02 問題一:低權限用戶密碼重置
第一個問題為`GetPasswordPolicy.js`的NoSQL注入,匿名用戶可通過發送密碼重置請求,爆破密碼重置Token修改任意低權限用戶密碼。`GetPasswordPolicy.js`的代碼如下,一般情況下`params`中存在token參數為字符串。

注意到`meteor`的數據模型默認是支持正則表達式匹配的,可參考`StackOverflow問題`。

為了構造一個`getPasswordPolicy`接口的合法調用,用burpsuite中攔截登錄請求,修改為正則匹配并完成測試。
#登錄Post請求{"message":"{\"msg\":\"method\",\"method\":\"login\",\"params\":[{\"user\":{\"email\":\"user1@testlocal.com\"},\"password\":{\"digest\":\"67727a41b5b1d4dfca981e4045b1bb2f1e7fef0e3e8825c028949d186cad4c00\",\"algorithm\":\"sha-256\"}}],\"id\":\"78\"}"}
#修改后的請求{"message":"{\"msg\":\"method\",\"method\":\"getPasswordPolicy\",\"params\":[{\"token\":{\"$regex\":\"^G\"}}]}"}
隨后便可通過正則表達式匹配,使用類似盲注的方法獲取指定用戶的密碼Token。

通過獲取的token即可實現對指定賬戶的密碼重置,下邊是利用過程。
首先通過登錄頁面密碼重置功能發送密碼重置請求。

接下來通過NoSQL注入暴力破解token,Rocket.chat存在接口限速,破解過程會比較慢。

使用token重置目標用戶密碼,處理代碼位于`resetPassword.js`。

查看html代碼輸入的參數主要有兩個,密碼和`token`。

發送http密碼重置請求。

值得一提的是:密碼重置僅限于低權限用戶,`admin`用戶重置密碼后會開啟TOTP(為什么會開啟尚且不知道原因),隨后登錄管理員時會強制輸入TOTP密碼。由于賬戶TOTP秘鑰不存在,無論如何是不會登錄成功的。

0x03 問題二:認證后用戶枚舉+管理員密碼重置
第二處問題依然是NoSQL注入,登錄用戶可通過`/api/v1/users.list`接口列舉其它用戶信息,并基于`TOTP`機制實現管理員的登錄。問題代碼位于`app/api/server/v1/users.js`,通過`Users.find`搜索用戶。

對于mongo數據庫, 攻擊者可通過`$where`語句注入`javascript`代碼,官方文檔接口說明如下圖所示。

通過傳入`query:{"$where":"this.username==='admin' && (()=>{ throw this.secret })()"}`,最終會在`users.list`接口中處理一下代碼,目的是查詢 `username=admin`的用戶并使用異常報錯輸出內容。
Users.find( { "$where":"this.username==='admin' && (()=>{ throw JSON.stringify(this) })()" }, {/*...*/}).fetch();
使用`/api/v1/users.list?query=%7B%22$where%22%3A%22this.username%3D%3D%3D'admin'+%26%26+(()%3D%3E%7B+throw+JSON.stringify(this)+%7D)()%22%7D`URL進行測試,可獲取指定用戶的信息,包括`hashedToken`、`加密hash`、`Email`等。

如果報文返回`{"success":false,"error":"unauthorized"}`,表示當前用戶權限不夠。通過測試得知提供的用戶須至少具備`Admin`或者`user`角色,由于注冊過的用戶默認具備`user`角色,所以問題并不大。

實際上,bcrypt密碼hash破解的難度非常大,所以這里不考慮破解密碼的可能性。公開利用中考慮了2FA認證的情況,開啟方法如下:
進入`Account`->`安全`->`啟用TOTP`認證,使用TOTP秘鑰或掃描二維碼完成首次驗證。二維碼掃描結果示例為`otpauth://totp/Rocket.Chat:admin?secret=GJICK3LRNVZV2NJ2FJDGSKKWKBREC3LLERWCMYLHIU5XI3LVORKA`。

使用TOTP工具生成登錄碼,認證成功后提示備份代碼。


設置完畢,繼續調用`users.list`接口,TOTP密碼被成功泄露。

正常用戶登錄則要求輸入`TOTP Code`。

最后使用admin重置的密碼和totp碼登錄系統,過程如下:
在成功獲取admin用戶密碼重置token和totp秘鑰的前提下,發送重置密碼報文。

使用新密碼登錄。

0x04 問題三:實現遠程命令執行
登錄系統后,在系統管理中新建一個集成項目。

設置集成名稱、發送頻道及身份。

設置JS腳本,參考案例如下。
const require = console.log.constructor('return process.mainModule.require')();const {exec} = require('child_process');exec('echo pwned>/tmp/pwn.txt');

點擊保存,拷貝curl命令,執行后命令被執行。

漏洞總結
通過補丁對比,詳細理解了Rocket.Chat遠程命令執行漏洞的原理。該漏洞的條件主要有:
- 管理員需開啟TOTP認證功能。
- 需已知一個普通用戶及管理員郵箱。當注冊功能開放時,可通過注冊功能直接生成一個低權限用戶。
- 命令執行借助的是后臺`集成`功能,默認開啟。
同時,當管理員未開啟`TOTP`認證功能時,可以通過普通用戶獲取管理員用戶加密Hash,但使用`bcrypt`加密,破解難度較大。
最后,記得更新Rocket.Chat到最新版本,下圖為一種版本檢測方法。

參考
https://blog.sonarsource.com/nosql-injections-in-rocket-chat
https://www.moerats.com/archives/530/