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/
