基础知识
YAPI是由去哪儿网移动架构组(简称YMFE,一群由FE、iOS和Android工程师共同组成的具想象力、创造 力和影响力的大前端团队)开发的可视化接口管理工具,是一个可本地部署的、打通前后端及QA的接口 管理平台。YAPI旨在为开发、产品和测试人员提供更优雅的接口管理服务,可以帮助开发者轻松创建、 发布和维护不同项目,不同平台的API。YAPI接口管理平台是国内某旅行网站的大前端技术中心开源项 目,为前端后台开发与测试人员提供更优雅的接口管理服务,该系统被国内较多知名互联网企业所采 用。
漏洞简介
环境搭建
利用 docker 来搭建漏洞环境 docker yyds
利用项目 https://github.com/fjc0k/docker-YApi 来搭建 docker
为了确定 docker 对应的版本信息 我们查看 选择版本 1.10.2
https://hub.docker.com/r/jayfong/yapi/tags
git clone https://github.com/fjc0k/docker-YApi cd docker-YApi vim docker-compose.yml # 修改 yapi 的版本 docker-compose up -d
访问 http://127.0.0.1:40001 账号名:admin@docker.yapi 密码:adm1n
创建成功之后需要创建项目 添加接口
当然也是可以直接利用 vulhub 上的这个项目,省去创建项目与添加接口的操作 https://github.com/vulhub/vulhub/tree/master/yapi/mongodb-inj
漏洞复现
直接利用 github 上的脚本进行复现 https://github.com/vulhub/vulhub/blob/master/yapi/mongodb-inj/poc.py
漏洞分析
根据漏洞描述,通过注入漏洞获取有效用户 token ,进而利用自动化测试接口绕过沙箱限制,最终在目标系统上执行任意命令。可以得出漏洞分为两个部分,第一部分是注入获取管理员 token ,第二部分是绕过沙箱限制实现命令执行。
通过查看 commits 记录
https://github.com/YMFE/yapi/commit/59bade3a8a43e7db077d38a4b0c7c584f30ddf8c
NoSQL 注入
根据修复记录, 推断出注入点应该为 token
这里的 openApiRouter
存在多个路径,因为、/api/open/ 开头的路由会到另一个分支 所以除路由 /api/open/run_auto_test
和 /api/open/import_data
应该都可触发
我们利用一个路由 /api/interface/add_cat
根据 https://hellosean1025.github.io/yapi/openapi.html 找到需要传入的参数
当传入的 token 为正确值时 返回数据
当传入的 token 未匹配到时,返回请登录
如此看来 此处就为 NoSQL 的盲注
首先调用 parseToken 对 token 进行处理
判断 token 不为空后 调用 aseDecode 进行解密,解密失败最终会返回 false
经过条件判断之后 进入 getProjectIdByToken
会根据传入的 token 出现项目 id
在 findId 中 最后会调用 mongdb 中的 findOne 查询数据。
这里我们注意到 是没有回显的盲注,仅能通过登录是否成功来进行判断
当页面没有回显时,那么我们可以通过 $regex
正则表达式来达到和 SQL 注入中substr()
函数相同的功能,而且 NoSQL 用到的基本上都是布尔盲注。
db.token.findOne({token: token});
利用 \$regex 正则匹配
db.token.findOne({token: {'\$regex':'^'}});
"token":{"\$regex":"^[a-z]"} #查询token 第一位是否是 a-z
"token":{"\$regex":"^[0-9]"} #查询token 第一位是否是 0-9
命令执行
注意到修复的方法是 关闭 Pre-request Script 和 Pre-response Script
其实本质上仍为yapi 2021 远程命令执行漏洞
java 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()