基于Yapi下的MongoDB NoSQL注入造成的RCE
某次打点时偶遇Yapi管理平台资产,发现其存在版本下 (< 1.12.0)
的nday漏洞,整个攻击过程感觉挺巧妙的,以此记录下对其白盒测试的分析和思考
环境搭建
1 | docker compose up -d |
漏洞复现
MongoDB的基础使用
MongoDB是一种非关系型数据库 (NoSQL) 是一类不采用传统表格结构来存储数据的数据库,它将数据以类似 JSON 的格式存储,每条记录就是一个文档。这种结构非常适合存储复杂、嵌套的数据
虽然这有点脱离这篇文章的重心,但我觉得还是可以简单了解一下这种数据库的特性
简单来说就是在一些游戏活动场景下,并不是每个用户都会参与活动,如果像关系型数据库那样,给每个用户都添加该活动名的字段,那有很多用户的该字段下一定是空的,这样就会造成大量空间浪费,况且游戏迭代非常频繁,每次都会修改表结构
ID | Name | 装备 | 春节活动 | 七夕活动 | 夏日活动 |
---|---|---|---|---|---|
1 | Hong | 剑 | 是 | ||
2 | Ming | 枪 | 是 | 是 | |
3 | Bai | 刀 | 是 |
但非关系型数据库就不会存在这样的情况,因为每一个文档就是一行数据,那多个文档对应的就是一个集合
1 | { |
show dbs;
找到yapi数据库,我们可以看到token集合的具体内容
对token的类型检验不严格导致nosql注入
在YApi中,每一个项目都有唯一标识的token,用户可以使用这个 token 值(加密后)来请求开放接口(无需登录),注意这不是用户登录的那个token;其项目的api接口就是需要这个项目token鉴权
我们关注server\controllers\base.js, line 62
首先会从请求包中获取query和body参数,并且检查token是否为空,这里也是漏洞点,没有检查token的类型 (修复版本中在这里强制检查要求token是字符串类型),接着通过parseToken()
函数来解析token,这里我们跟进到server\utils\token.js
可以看到采用了aes192的加解密算法,并且如果没有在config.js中配置盐,则会使用const defaultSalt = 'abcde';
这里我们可以看到token的具体结构,uid + '|' + token
,具体来说这里是你的项目token
返回到server\controllers\base.js, line 87
,跟进getProjectIdByToken()
,这里会使用MongoDB数据库中的findone方法查询project_id
整个逻辑分析完了,接下来就是在刚才的漏洞点进行json格式的注入
首先是对project的token注入,这里我们要找到一些可用于接收application/json
参数类型的接口,/api/interface/up
就是一个很好的选择,这里使用正则表达式语法
1 | {"id": -1, "token": {"$regex": "^1"}} |
vulhub中提供的Poc可以看到脚本原理也是如此
当匹配时
不匹配时
形成了布尔盲注
之后就是遍历UID去构造encrypted token,不过这里前提在于使用了defaultSalt
上传并运行vm2逃逸脚本造成RCE
有了加密token就可以干很多事情了,当然最直接严重的就是命令执行,其中请求配置可以上传自定义 js 脚本,其中会用到VM2沙箱,在未登录的情况下可以使用加密token通过/api/project/up
路由上传
接着通过/api/open/run_auto_test?token=&id=project_id&mode=json
运行
至于VM2沙箱逃逸原理以前分析过一个Proxy、递归栈溢出 和 异常处理来实现逃逸的AWDP代码,但是逃逸方法种类繁多,以后再统一写篇文章分析一下吧